blob: 9024d783df2bc316b802e907326cdc7fad9ceb8d [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;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002233 for (p = start; *p && *p != ':' && *p != '%'; p++)
2234 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002235 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2236 while (stackblocksize() < len)
2237 growstackblock();
2238 q = stackblock();
2239 if (p != start) {
2240 memcpy(q, start, p - start);
2241 q += p - start;
2242 *q++ = '/';
2243 }
2244 strcpy(q, name);
2245 pathopt = NULL;
2246 if (*p == '%') {
2247 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002248 while (*p && *p != ':')
2249 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002250 }
2251 if (*p == ':')
2252 *path = p + 1;
2253 else
2254 *path = NULL;
2255 return stalloc(len);
2256}
2257
2258
2259/* ============ Prompt */
2260
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002261static smallint doprompt; /* if set, prompt the user */
2262static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002263
2264#if ENABLE_FEATURE_EDITING
2265static line_input_t *line_input_state;
2266static const char *cmdedit_prompt;
2267static void
2268putprompt(const char *s)
2269{
2270 if (ENABLE_ASH_EXPAND_PRMT) {
2271 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002272 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002273 return;
2274 }
2275 cmdedit_prompt = s;
2276}
2277#else
2278static void
2279putprompt(const char *s)
2280{
2281 out2str(s);
2282}
2283#endif
2284
2285#if ENABLE_ASH_EXPAND_PRMT
2286/* expandstr() needs parsing machinery, so it is far away ahead... */
2287static const char *expandstr(const char *ps);
2288#else
2289#define expandstr(s) s
2290#endif
2291
2292static void
2293setprompt(int whichprompt)
2294{
2295 const char *prompt;
2296#if ENABLE_ASH_EXPAND_PRMT
2297 struct stackmark smark;
2298#endif
2299
2300 needprompt = 0;
2301
2302 switch (whichprompt) {
2303 case 1:
2304 prompt = ps1val();
2305 break;
2306 case 2:
2307 prompt = ps2val();
2308 break;
2309 default: /* 0 */
2310 prompt = nullstr;
2311 }
2312#if ENABLE_ASH_EXPAND_PRMT
2313 setstackmark(&smark);
2314 stalloc(stackblocksize());
2315#endif
2316 putprompt(expandstr(prompt));
2317#if ENABLE_ASH_EXPAND_PRMT
2318 popstackmark(&smark);
2319#endif
2320}
2321
2322
2323/* ============ The cd and pwd commands */
2324
2325#define CD_PHYSICAL 1
2326#define CD_PRINT 2
2327
2328static int docd(const char *, int);
2329
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002330static int
2331cdopt(void)
2332{
2333 int flags = 0;
2334 int i, j;
2335
2336 j = 'L';
2337 while ((i = nextopt("LP"))) {
2338 if (i != j) {
2339 flags ^= CD_PHYSICAL;
2340 j = i;
2341 }
2342 }
2343
2344 return flags;
2345}
2346
2347/*
2348 * Update curdir (the name of the current directory) in response to a
2349 * cd command.
2350 */
2351static const char *
2352updatepwd(const char *dir)
2353{
2354 char *new;
2355 char *p;
2356 char *cdcomppath;
2357 const char *lim;
2358
2359 cdcomppath = ststrdup(dir);
2360 STARTSTACKSTR(new);
2361 if (*dir != '/') {
2362 if (curdir == nullstr)
2363 return 0;
2364 new = stack_putstr(curdir, new);
2365 }
2366 new = makestrspace(strlen(dir) + 2, new);
2367 lim = stackblock() + 1;
2368 if (*dir != '/') {
2369 if (new[-1] != '/')
2370 USTPUTC('/', new);
2371 if (new > lim && *lim == '/')
2372 lim++;
2373 } else {
2374 USTPUTC('/', new);
2375 cdcomppath++;
2376 if (dir[1] == '/' && dir[2] != '/') {
2377 USTPUTC('/', new);
2378 cdcomppath++;
2379 lim++;
2380 }
2381 }
2382 p = strtok(cdcomppath, "/");
2383 while (p) {
2384 switch (*p) {
2385 case '.':
2386 if (p[1] == '.' && p[2] == '\0') {
2387 while (new > lim) {
2388 STUNPUTC(new);
2389 if (new[-1] == '/')
2390 break;
2391 }
2392 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002393 }
2394 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002395 break;
2396 /* fall through */
2397 default:
2398 new = stack_putstr(p, new);
2399 USTPUTC('/', new);
2400 }
2401 p = strtok(0, "/");
2402 }
2403 if (new > lim)
2404 STUNPUTC(new);
2405 *new = 0;
2406 return stackblock();
2407}
2408
2409/*
2410 * Find out what the current directory is. If we already know the current
2411 * directory, this routine returns immediately.
2412 */
2413static char *
2414getpwd(void)
2415{
Denis Vlasenko01631112007-12-16 17:20:38 +00002416 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002417 return dir ? dir : nullstr;
2418}
2419
2420static void
2421setpwd(const char *val, int setold)
2422{
2423 char *oldcur, *dir;
2424
2425 oldcur = dir = curdir;
2426
2427 if (setold) {
2428 setvar("OLDPWD", oldcur, VEXPORT);
2429 }
2430 INT_OFF;
2431 if (physdir != nullstr) {
2432 if (physdir != oldcur)
2433 free(physdir);
2434 physdir = nullstr;
2435 }
2436 if (oldcur == val || !val) {
2437 char *s = getpwd();
2438 physdir = s;
2439 if (!val)
2440 dir = s;
2441 } else
2442 dir = ckstrdup(val);
2443 if (oldcur != dir && oldcur != nullstr) {
2444 free(oldcur);
2445 }
2446 curdir = dir;
2447 INT_ON;
2448 setvar("PWD", dir, VEXPORT);
2449}
2450
2451static void hashcd(void);
2452
2453/*
2454 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2455 * know that the current directory has changed.
2456 */
2457static int
2458docd(const char *dest, int flags)
2459{
2460 const char *dir = 0;
2461 int err;
2462
2463 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2464
2465 INT_OFF;
2466 if (!(flags & CD_PHYSICAL)) {
2467 dir = updatepwd(dest);
2468 if (dir)
2469 dest = dir;
2470 }
2471 err = chdir(dest);
2472 if (err)
2473 goto out;
2474 setpwd(dir, 1);
2475 hashcd();
2476 out:
2477 INT_ON;
2478 return err;
2479}
2480
2481static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002482cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002483{
2484 const char *dest;
2485 const char *path;
2486 const char *p;
2487 char c;
2488 struct stat statb;
2489 int flags;
2490
2491 flags = cdopt();
2492 dest = *argptr;
2493 if (!dest)
2494 dest = bltinlookup(homestr);
2495 else if (LONE_DASH(dest)) {
2496 dest = bltinlookup("OLDPWD");
2497 flags |= CD_PRINT;
2498 }
2499 if (!dest)
2500 dest = nullstr;
2501 if (*dest == '/')
2502 goto step7;
2503 if (*dest == '.') {
2504 c = dest[1];
2505 dotdot:
2506 switch (c) {
2507 case '\0':
2508 case '/':
2509 goto step6;
2510 case '.':
2511 c = dest[2];
2512 if (c != '.')
2513 goto dotdot;
2514 }
2515 }
2516 if (!*dest)
2517 dest = ".";
2518 path = bltinlookup("CDPATH");
2519 if (!path) {
2520 step6:
2521 step7:
2522 p = dest;
2523 goto docd;
2524 }
2525 do {
2526 c = *path;
2527 p = padvance(&path, dest);
2528 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2529 if (c && c != ':')
2530 flags |= CD_PRINT;
2531 docd:
2532 if (!docd(p, flags))
2533 goto out;
2534 break;
2535 }
2536 } while (path);
2537 ash_msg_and_raise_error("can't cd to %s", dest);
2538 /* NOTREACHED */
2539 out:
2540 if (flags & CD_PRINT)
2541 out1fmt(snlfmt, curdir);
2542 return 0;
2543}
2544
2545static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00002546pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002547{
2548 int flags;
2549 const char *dir = curdir;
2550
2551 flags = cdopt();
2552 if (flags) {
2553 if (physdir == nullstr)
2554 setpwd(dir, 0);
2555 dir = physdir;
2556 }
2557 out1fmt(snlfmt, dir);
2558 return 0;
2559}
2560
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002561
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002562/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002563
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002564#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002565#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002566
Eric Andersenc470f442003-07-28 09:56:35 +00002567/* Syntax classes */
2568#define CWORD 0 /* character is nothing special */
2569#define CNL 1 /* newline character */
2570#define CBACK 2 /* a backslash character */
2571#define CSQUOTE 3 /* single quote */
2572#define CDQUOTE 4 /* double quote */
2573#define CENDQUOTE 5 /* a terminating quote */
2574#define CBQUOTE 6 /* backwards single quote */
2575#define CVAR 7 /* a dollar sign */
2576#define CENDVAR 8 /* a '}' character */
2577#define CLP 9 /* a left paren in arithmetic */
2578#define CRP 10 /* a right paren in arithmetic */
2579#define CENDFILE 11 /* end of file */
2580#define CCTL 12 /* like CWORD, except it must be escaped */
2581#define CSPCL 13 /* these terminate a word */
2582#define CIGN 14 /* character should be ignored */
2583
Denis Vlasenko131ae172007-02-18 13:00:19 +00002584#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002585#define SYNBASE 130
2586#define PEOF -130
2587#define PEOA -129
2588#define PEOA_OR_PEOF PEOA
2589#else
2590#define SYNBASE 129
2591#define PEOF -129
2592#define PEOA_OR_PEOF PEOF
2593#endif
2594
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002595/* number syntax index */
2596#define BASESYNTAX 0 /* not in quotes */
2597#define DQSYNTAX 1 /* in double quotes */
2598#define SQSYNTAX 2 /* in single quotes */
2599#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002600#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002601
Denis Vlasenko131ae172007-02-18 13:00:19 +00002602#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002603#define USE_SIT_FUNCTION
2604#endif
2605
Denis Vlasenko131ae172007-02-18 13:00:19 +00002606#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002607static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002608#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002610#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002611 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2612 { CNL, CNL, CNL, CNL }, /* 2, \n */
2613 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2614 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2615 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2616 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2617 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2618 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2619 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2620 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2621 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002622#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002623 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2624 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2625 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002626#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002627};
Eric Andersenc470f442003-07-28 09:56:35 +00002628#else
2629static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002631 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002632#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002633 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2634 { CNL, CNL, CNL }, /* 2, \n */
2635 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2636 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2637 { CVAR, CVAR, CWORD }, /* 5, $ */
2638 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2639 { CSPCL, CWORD, CWORD }, /* 7, ( */
2640 { CSPCL, CWORD, CWORD }, /* 8, ) */
2641 { CBACK, CBACK, CCTL }, /* 9, \ */
2642 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2643 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002644#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002645 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2646 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2647 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002648#endif
2649};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002650#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002651
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002652#ifdef USE_SIT_FUNCTION
2653
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002654static int
2655SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002657 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002658#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002659 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002660 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2661 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2662 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2663 11, 3 /* "}~" */
2664 };
2665#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002666 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002667 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2668 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2669 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2670 10, 2 /* "}~" */
2671 };
2672#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 const char *s;
2674 int indx;
2675
Eric Andersenc470f442003-07-28 09:56:35 +00002676 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002677 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002678#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002679 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002680 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002681 else
2682#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002683#define U_C(c) ((unsigned char)(c))
2684
2685 if ((unsigned char)c >= (unsigned char)(CTLESC)
2686 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2687 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002688 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002689 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002691 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002692 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002693 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002694 }
2695 return S_I_T[indx][syntax];
2696}
2697
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002698#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002699
Denis Vlasenko131ae172007-02-18 13:00:19 +00002700#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002701#define CSPCL_CIGN_CIGN_CIGN 0
2702#define CSPCL_CWORD_CWORD_CWORD 1
2703#define CNL_CNL_CNL_CNL 2
2704#define CWORD_CCTL_CCTL_CWORD 3
2705#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2706#define CVAR_CVAR_CWORD_CVAR 5
2707#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2708#define CSPCL_CWORD_CWORD_CLP 7
2709#define CSPCL_CWORD_CWORD_CRP 8
2710#define CBACK_CBACK_CCTL_CBACK 9
2711#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2712#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2713#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2714#define CWORD_CWORD_CWORD_CWORD 13
2715#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002716#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002717#define CSPCL_CWORD_CWORD_CWORD 0
2718#define CNL_CNL_CNL_CNL 1
2719#define CWORD_CCTL_CCTL_CWORD 2
2720#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2721#define CVAR_CVAR_CWORD_CVAR 4
2722#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2723#define CSPCL_CWORD_CWORD_CLP 6
2724#define CSPCL_CWORD_CWORD_CRP 7
2725#define CBACK_CBACK_CCTL_CBACK 8
2726#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2727#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2728#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2729#define CWORD_CWORD_CWORD_CWORD 12
2730#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002731#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002732
2733static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002734 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002735 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002736#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002737 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2738#endif
2739 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2741 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2742 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2743 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2744 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2745 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2746 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2747 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002748 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2878 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2900 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002901 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002902 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2904 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002906 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002907 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2908 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2909 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2912 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2913 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2914 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2927 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2928 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2929 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2930 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2931 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2959 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2960 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2961 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2964 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2992 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2993 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2994 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002995};
2996
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002997#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2998
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002999#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003000
Eric Andersen2870d962001-07-02 17:27:21 +00003001
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003002/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003003
Denis Vlasenko131ae172007-02-18 13:00:19 +00003004#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003005
3006#define ALIASINUSE 1
3007#define ALIASDEAD 2
3008
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003009struct alias {
3010 struct alias *next;
3011 char *name;
3012 char *val;
3013 int flag;
3014};
3015
Denis Vlasenko01631112007-12-16 17:20:38 +00003016
3017static struct alias **atab; // [ATABSIZE];
3018#define INIT_G_alias() do { \
3019 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3020} while (0)
3021
Eric Andersen2870d962001-07-02 17:27:21 +00003022
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003023static struct alias **
3024__lookupalias(const char *name) {
3025 unsigned int hashval;
3026 struct alias **app;
3027 const char *p;
3028 unsigned int ch;
3029
3030 p = name;
3031
3032 ch = (unsigned char)*p;
3033 hashval = ch << 4;
3034 while (ch) {
3035 hashval += ch;
3036 ch = (unsigned char)*++p;
3037 }
3038 app = &atab[hashval % ATABSIZE];
3039
3040 for (; *app; app = &(*app)->next) {
3041 if (strcmp(name, (*app)->name) == 0) {
3042 break;
3043 }
3044 }
3045
3046 return app;
3047}
3048
3049static struct alias *
3050lookupalias(const char *name, int check)
3051{
3052 struct alias *ap = *__lookupalias(name);
3053
3054 if (check && ap && (ap->flag & ALIASINUSE))
3055 return NULL;
3056 return ap;
3057}
3058
3059static struct alias *
3060freealias(struct alias *ap)
3061{
3062 struct alias *next;
3063
3064 if (ap->flag & ALIASINUSE) {
3065 ap->flag |= ALIASDEAD;
3066 return ap;
3067 }
3068
3069 next = ap->next;
3070 free(ap->name);
3071 free(ap->val);
3072 free(ap);
3073 return next;
3074}
Eric Andersencb57d552001-06-28 07:25:16 +00003075
Eric Andersenc470f442003-07-28 09:56:35 +00003076static void
3077setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003078{
3079 struct alias *ap, **app;
3080
3081 app = __lookupalias(name);
3082 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003083 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003084 if (ap) {
3085 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003086 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003087 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003088 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003089 ap->flag &= ~ALIASDEAD;
3090 } else {
3091 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003092 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003093 ap->name = ckstrdup(name);
3094 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003095 /*ap->flag = 0; - ckzalloc did it */
3096 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003097 *app = ap;
3098 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003099 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003100}
3101
Eric Andersenc470f442003-07-28 09:56:35 +00003102static int
3103unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003104{
Eric Andersencb57d552001-06-28 07:25:16 +00003105 struct alias **app;
3106
3107 app = __lookupalias(name);
3108
3109 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003110 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003111 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003112 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003113 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003114 }
3115
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003116 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003117}
3118
Eric Andersenc470f442003-07-28 09:56:35 +00003119static void
3120rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003121{
Eric Andersencb57d552001-06-28 07:25:16 +00003122 struct alias *ap, **app;
3123 int i;
3124
Denis Vlasenkob012b102007-02-19 22:43:01 +00003125 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003126 for (i = 0; i < ATABSIZE; i++) {
3127 app = &atab[i];
3128 for (ap = *app; ap; ap = *app) {
3129 *app = freealias(*app);
3130 if (ap == *app) {
3131 app = &ap->next;
3132 }
3133 }
3134 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003135 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003136}
3137
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003138static void
3139printalias(const struct alias *ap)
3140{
3141 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3142}
3143
Eric Andersencb57d552001-06-28 07:25:16 +00003144/*
3145 * TODO - sort output
3146 */
Eric Andersenc470f442003-07-28 09:56:35 +00003147static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003148aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003149{
3150 char *n, *v;
3151 int ret = 0;
3152 struct alias *ap;
3153
Denis Vlasenko68404f12008-03-17 09:00:54 +00003154 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003155 int i;
3156
Denis Vlasenko68404f12008-03-17 09:00:54 +00003157 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003158 for (ap = atab[i]; ap; ap = ap->next) {
3159 printalias(ap);
3160 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003161 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003162 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003163 }
3164 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003165 v = strchr(n+1, '=');
3166 if (v == NULL) { /* n+1: funny ksh stuff */
3167 ap = *__lookupalias(n);
3168 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003169 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003170 ret = 1;
3171 } else
3172 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003173 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003174 *v++ = '\0';
3175 setalias(n, v);
3176 }
3177 }
3178
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003179 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003180}
3181
Eric Andersenc470f442003-07-28 09:56:35 +00003182static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003183unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00003184{
3185 int i;
3186
3187 while ((i = nextopt("a")) != '\0') {
3188 if (i == 'a') {
3189 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003190 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003191 }
3192 }
3193 for (i = 0; *argptr; argptr++) {
3194 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003195 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003196 i = 1;
3197 }
3198 }
3199
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003200 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003201}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003202
Denis Vlasenko131ae172007-02-18 13:00:19 +00003203#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003204
Eric Andersenc470f442003-07-28 09:56:35 +00003205
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003206/* ============ jobs.c */
3207
3208/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3209#define FORK_FG 0
3210#define FORK_BG 1
3211#define FORK_NOJOB 2
3212
3213/* mode flags for showjob(s) */
3214#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3215#define SHOW_PID 0x04 /* include process pid */
3216#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3217
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003218/*
3219 * A job structure contains information about a job. A job is either a
3220 * single process or a set of processes contained in a pipeline. In the
3221 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3222 * array of pids.
3223 */
3224
3225struct procstat {
3226 pid_t pid; /* process id */
3227 int status; /* last process status from wait() */
3228 char *cmd; /* text of command being run */
3229};
3230
3231struct job {
3232 struct procstat ps0; /* status of process */
3233 struct procstat *ps; /* status or processes when more than one */
3234#if JOBS
3235 int stopstatus; /* status of a stopped job */
3236#endif
3237 uint32_t
3238 nprocs: 16, /* number of processes */
3239 state: 8,
3240#define JOBRUNNING 0 /* at least one proc running */
3241#define JOBSTOPPED 1 /* all procs are stopped */
3242#define JOBDONE 2 /* all procs are completed */
3243#if JOBS
3244 sigint: 1, /* job was killed by SIGINT */
3245 jobctl: 1, /* job running under job control */
3246#endif
3247 waited: 1, /* true if this entry has been waited for */
3248 used: 1, /* true if this entry is in used */
3249 changed: 1; /* true if status has changed */
3250 struct job *prev_job; /* previous job */
3251};
3252
3253static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003254static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003255
Denis Vlasenko68404f12008-03-17 09:00:54 +00003256static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003257#if !JOBS
3258#define forkshell(job, node, mode) forkshell(job, mode)
3259#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003260static int forkshell(struct job *, union node *, int);
3261static int waitforjob(struct job *);
3262
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003263#if !JOBS
3264enum { jobctl = 0 };
3265#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003266#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003267static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003268static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003269#endif
3270
3271/*
3272 * Set the signal handler for the specified signal. The routine figures
3273 * out what it should be set to.
3274 */
3275static void
3276setsignal(int signo)
3277{
3278 int action;
3279 char *t, tsig;
3280 struct sigaction act;
3281
3282 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003283 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003284 if (t == NULL)
3285 action = S_DFL;
3286 else if (*t != '\0')
3287 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003288 if (rootshell && action == S_DFL) {
3289 switch (signo) {
3290 case SIGINT:
3291 if (iflag || minusc || sflag == 0)
3292 action = S_CATCH;
3293 break;
3294 case SIGQUIT:
3295#if DEBUG
3296 if (debug)
3297 break;
3298#endif
3299 /* FALLTHROUGH */
3300 case SIGTERM:
3301 if (iflag)
3302 action = S_IGN;
3303 break;
3304#if JOBS
3305 case SIGTSTP:
3306 case SIGTTOU:
3307 if (mflag)
3308 action = S_IGN;
3309 break;
3310#endif
3311 }
3312 }
3313
3314 t = &sigmode[signo - 1];
3315 tsig = *t;
3316 if (tsig == 0) {
3317 /*
3318 * current setting unknown
3319 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003320 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321 /*
3322 * Pretend it worked; maybe we should give a warning
3323 * here, but other shells don't. We don't alter
3324 * sigmode, so that we retry every time.
3325 */
3326 return;
3327 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003328 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003330 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331 if (mflag
3332 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3333 ) {
3334 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003335 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336 }
3337 }
3338 if (tsig == S_HARD_IGN || tsig == action)
3339 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003340 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003341 switch (action) {
3342 case S_CATCH:
3343 act.sa_handler = onsig;
3344 break;
3345 case S_IGN:
3346 act.sa_handler = SIG_IGN;
3347 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348 }
3349 *t = action;
3350 act.sa_flags = 0;
3351 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003352 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353}
3354
3355/* mode flags for set_curjob */
3356#define CUR_DELETE 2
3357#define CUR_RUNNING 1
3358#define CUR_STOPPED 0
3359
3360/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003361#define DOWAIT_NONBLOCK WNOHANG
3362#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003363
3364#if JOBS
3365/* pgrp of shell on invocation */
3366static int initialpgrp;
3367static int ttyfd = -1;
3368#endif
3369/* array of jobs */
3370static struct job *jobtab;
3371/* size of array */
3372static unsigned njobs;
3373/* current job */
3374static struct job *curjob;
3375/* number of presumed living untracked jobs */
3376static int jobless;
3377
3378static void
3379set_curjob(struct job *jp, unsigned mode)
3380{
3381 struct job *jp1;
3382 struct job **jpp, **curp;
3383
3384 /* first remove from list */
3385 jpp = curp = &curjob;
3386 do {
3387 jp1 = *jpp;
3388 if (jp1 == jp)
3389 break;
3390 jpp = &jp1->prev_job;
3391 } while (1);
3392 *jpp = jp1->prev_job;
3393
3394 /* Then re-insert in correct position */
3395 jpp = curp;
3396 switch (mode) {
3397 default:
3398#if DEBUG
3399 abort();
3400#endif
3401 case CUR_DELETE:
3402 /* job being deleted */
3403 break;
3404 case CUR_RUNNING:
3405 /* newly created job or backgrounded job,
3406 put after all stopped jobs. */
3407 do {
3408 jp1 = *jpp;
3409#if JOBS
3410 if (!jp1 || jp1->state != JOBSTOPPED)
3411#endif
3412 break;
3413 jpp = &jp1->prev_job;
3414 } while (1);
3415 /* FALLTHROUGH */
3416#if JOBS
3417 case CUR_STOPPED:
3418#endif
3419 /* newly stopped job - becomes curjob */
3420 jp->prev_job = *jpp;
3421 *jpp = jp;
3422 break;
3423 }
3424}
3425
3426#if JOBS || DEBUG
3427static int
3428jobno(const struct job *jp)
3429{
3430 return jp - jobtab + 1;
3431}
3432#endif
3433
3434/*
3435 * Convert a job name to a job structure.
3436 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003437#if !JOBS
3438#define getjob(name, getctl) getjob(name)
3439#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440static struct job *
3441getjob(const char *name, int getctl)
3442{
3443 struct job *jp;
3444 struct job *found;
3445 const char *err_msg = "No such job: %s";
3446 unsigned num;
3447 int c;
3448 const char *p;
3449 char *(*match)(const char *, const char *);
3450
3451 jp = curjob;
3452 p = name;
3453 if (!p)
3454 goto currentjob;
3455
3456 if (*p != '%')
3457 goto err;
3458
3459 c = *++p;
3460 if (!c)
3461 goto currentjob;
3462
3463 if (!p[1]) {
3464 if (c == '+' || c == '%') {
3465 currentjob:
3466 err_msg = "No current job";
3467 goto check;
3468 }
3469 if (c == '-') {
3470 if (jp)
3471 jp = jp->prev_job;
3472 err_msg = "No previous job";
3473 check:
3474 if (!jp)
3475 goto err;
3476 goto gotit;
3477 }
3478 }
3479
3480 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003481// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003482 num = atoi(p);
3483 if (num < njobs) {
3484 jp = jobtab + num - 1;
3485 if (jp->used)
3486 goto gotit;
3487 goto err;
3488 }
3489 }
3490
3491 match = prefix;
3492 if (*p == '?') {
3493 match = strstr;
3494 p++;
3495 }
3496
3497 found = 0;
3498 while (1) {
3499 if (!jp)
3500 goto err;
3501 if (match(jp->ps[0].cmd, p)) {
3502 if (found)
3503 goto err;
3504 found = jp;
3505 err_msg = "%s: ambiguous";
3506 }
3507 jp = jp->prev_job;
3508 }
3509
3510 gotit:
3511#if JOBS
3512 err_msg = "job %s not created under job control";
3513 if (getctl && jp->jobctl == 0)
3514 goto err;
3515#endif
3516 return jp;
3517 err:
3518 ash_msg_and_raise_error(err_msg, name);
3519}
3520
3521/*
3522 * Mark a job structure as unused.
3523 */
3524static void
3525freejob(struct job *jp)
3526{
3527 struct procstat *ps;
3528 int i;
3529
3530 INT_OFF;
3531 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3532 if (ps->cmd != nullstr)
3533 free(ps->cmd);
3534 }
3535 if (jp->ps != &jp->ps0)
3536 free(jp->ps);
3537 jp->used = 0;
3538 set_curjob(jp, CUR_DELETE);
3539 INT_ON;
3540}
3541
3542#if JOBS
3543static void
3544xtcsetpgrp(int fd, pid_t pgrp)
3545{
3546 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003547 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548}
3549
3550/*
3551 * Turn job control on and off.
3552 *
3553 * Note: This code assumes that the third arg to ioctl is a character
3554 * pointer, which is true on Berkeley systems but not System V. Since
3555 * System V doesn't have job control yet, this isn't a problem now.
3556 *
3557 * Called with interrupts off.
3558 */
3559static void
3560setjobctl(int on)
3561{
3562 int fd;
3563 int pgrp;
3564
3565 if (on == jobctl || rootshell == 0)
3566 return;
3567 if (on) {
3568 int ofd;
3569 ofd = fd = open(_PATH_TTY, O_RDWR);
3570 if (fd < 0) {
3571 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3572 * That sometimes helps to acquire controlling tty.
3573 * Obviously, a workaround for bugs when someone
3574 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003575 fd = 2;
3576 while (!isatty(fd))
3577 if (--fd < 0)
3578 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003579 }
3580 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003581 if (ofd >= 0)
3582 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003583 if (fd < 0)
3584 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003585 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003586 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003587 do { /* while we are in the background */
3588 pgrp = tcgetpgrp(fd);
3589 if (pgrp < 0) {
3590 out:
3591 ash_msg("can't access tty; job control turned off");
3592 mflag = on = 0;
3593 goto close;
3594 }
3595 if (pgrp == getpgrp())
3596 break;
3597 killpg(0, SIGTTIN);
3598 } while (1);
3599 initialpgrp = pgrp;
3600
3601 setsignal(SIGTSTP);
3602 setsignal(SIGTTOU);
3603 setsignal(SIGTTIN);
3604 pgrp = rootpid;
3605 setpgid(0, pgrp);
3606 xtcsetpgrp(fd, pgrp);
3607 } else {
3608 /* turning job control off */
3609 fd = ttyfd;
3610 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003611 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003612 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003613 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614 setpgid(0, pgrp);
3615 setsignal(SIGTSTP);
3616 setsignal(SIGTTOU);
3617 setsignal(SIGTTIN);
3618 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003619 if (fd >= 0)
3620 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621 fd = -1;
3622 }
3623 ttyfd = fd;
3624 jobctl = on;
3625}
3626
3627static int
3628killcmd(int argc, char **argv)
3629{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003630 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003631 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003632 do {
3633 if (argv[i][0] == '%') {
3634 struct job *jp = getjob(argv[i], 0);
3635 unsigned pid = jp->ps[0].pid;
3636 /* Enough space for ' -NNN<nul>' */
3637 argv[i] = alloca(sizeof(int)*3 + 3);
3638 /* kill_main has matching code to expect
3639 * leading space. Needed to not confuse
3640 * negative pids with "kill -SIGNAL_NO" syntax */
3641 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003642 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003643 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003644 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003645 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003646}
3647
3648static void
3649showpipe(struct job *jp, FILE *out)
3650{
3651 struct procstat *sp;
3652 struct procstat *spend;
3653
3654 spend = jp->ps + jp->nprocs;
3655 for (sp = jp->ps + 1; sp < spend; sp++)
3656 fprintf(out, " | %s", sp->cmd);
3657 outcslow('\n', out);
3658 flush_stdout_stderr();
3659}
3660
3661
3662static int
3663restartjob(struct job *jp, int mode)
3664{
3665 struct procstat *ps;
3666 int i;
3667 int status;
3668 pid_t pgid;
3669
3670 INT_OFF;
3671 if (jp->state == JOBDONE)
3672 goto out;
3673 jp->state = JOBRUNNING;
3674 pgid = jp->ps->pid;
3675 if (mode == FORK_FG)
3676 xtcsetpgrp(ttyfd, pgid);
3677 killpg(pgid, SIGCONT);
3678 ps = jp->ps;
3679 i = jp->nprocs;
3680 do {
3681 if (WIFSTOPPED(ps->status)) {
3682 ps->status = -1;
3683 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003684 ps++;
3685 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686 out:
3687 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3688 INT_ON;
3689 return status;
3690}
3691
3692static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003693fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003694{
3695 struct job *jp;
3696 FILE *out;
3697 int mode;
3698 int retval;
3699
3700 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3701 nextopt(nullstr);
3702 argv = argptr;
3703 out = stdout;
3704 do {
3705 jp = getjob(*argv, 1);
3706 if (mode == FORK_BG) {
3707 set_curjob(jp, CUR_RUNNING);
3708 fprintf(out, "[%d] ", jobno(jp));
3709 }
3710 outstr(jp->ps->cmd, out);
3711 showpipe(jp, out);
3712 retval = restartjob(jp, mode);
3713 } while (*argv && *++argv);
3714 return retval;
3715}
3716#endif
3717
3718static int
3719sprint_status(char *s, int status, int sigonly)
3720{
3721 int col;
3722 int st;
3723
3724 col = 0;
3725 if (!WIFEXITED(status)) {
3726#if JOBS
3727 if (WIFSTOPPED(status))
3728 st = WSTOPSIG(status);
3729 else
3730#endif
3731 st = WTERMSIG(status);
3732 if (sigonly) {
3733 if (st == SIGINT || st == SIGPIPE)
3734 goto out;
3735#if JOBS
3736 if (WIFSTOPPED(status))
3737 goto out;
3738#endif
3739 }
3740 st &= 0x7f;
3741 col = fmtstr(s, 32, strsignal(st));
3742 if (WCOREDUMP(status)) {
3743 col += fmtstr(s + col, 16, " (core dumped)");
3744 }
3745 } else if (!sigonly) {
3746 st = WEXITSTATUS(status);
3747 if (st)
3748 col = fmtstr(s, 16, "Done(%d)", st);
3749 else
3750 col = fmtstr(s, 16, "Done");
3751 }
3752 out:
3753 return col;
3754}
3755
3756/*
3757 * Do a wait system call. If job control is compiled in, we accept
3758 * stopped processes. If block is zero, we return a value of zero
3759 * rather than blocking.
3760 *
3761 * System V doesn't have a non-blocking wait system call. It does
3762 * have a SIGCLD signal that is sent to a process when one of it's
3763 * children dies. The obvious way to use SIGCLD would be to install
3764 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3765 * was received, and have waitproc bump another counter when it got
3766 * the status of a process. Waitproc would then know that a wait
3767 * system call would not block if the two counters were different.
3768 * This approach doesn't work because if a process has children that
3769 * have not been waited for, System V will send it a SIGCLD when it
3770 * installs a signal handler for SIGCLD. What this means is that when
3771 * a child exits, the shell will be sent SIGCLD signals continuously
3772 * until is runs out of stack space, unless it does a wait call before
3773 * restoring the signal handler. The code below takes advantage of
3774 * this (mis)feature by installing a signal handler for SIGCLD and
3775 * then checking to see whether it was called. If there are any
3776 * children to be waited for, it will be.
3777 *
3778 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3779 * waits at all. In this case, the user will not be informed when
3780 * a background process until the next time she runs a real program
3781 * (as opposed to running a builtin command or just typing return),
3782 * and the jobs command may give out of date information.
3783 */
3784static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003785waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787#if JOBS
3788 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003789 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003791 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3792 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003793}
3794
3795/*
3796 * Wait for a process to terminate.
3797 */
3798static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003799dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800{
3801 int pid;
3802 int status;
3803 struct job *jp;
3804 struct job *thisjob;
3805 int state;
3806
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003807 TRACE(("dowait(%d) called\n", wait_flags));
3808 pid = waitproc(wait_flags, &status);
3809 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003810 if (pid <= 0) {
3811 /* If we were doing blocking wait and (probably) got EINTR,
3812 * check for pending sigs received while waiting.
3813 * (NB: can be moved into callers if needed) */
3814 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3815 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003816 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003817 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003818 INT_OFF;
3819 thisjob = NULL;
3820 for (jp = curjob; jp; jp = jp->prev_job) {
3821 struct procstat *sp;
3822 struct procstat *spend;
3823 if (jp->state == JOBDONE)
3824 continue;
3825 state = JOBDONE;
3826 spend = jp->ps + jp->nprocs;
3827 sp = jp->ps;
3828 do {
3829 if (sp->pid == pid) {
3830 TRACE(("Job %d: changing status of proc %d "
3831 "from 0x%x to 0x%x\n",
3832 jobno(jp), pid, sp->status, status));
3833 sp->status = status;
3834 thisjob = jp;
3835 }
3836 if (sp->status == -1)
3837 state = JOBRUNNING;
3838#if JOBS
3839 if (state == JOBRUNNING)
3840 continue;
3841 if (WIFSTOPPED(sp->status)) {
3842 jp->stopstatus = sp->status;
3843 state = JOBSTOPPED;
3844 }
3845#endif
3846 } while (++sp < spend);
3847 if (thisjob)
3848 goto gotjob;
3849 }
3850#if JOBS
3851 if (!WIFSTOPPED(status))
3852#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003853 jobless--;
3854 goto out;
3855
3856 gotjob:
3857 if (state != JOBRUNNING) {
3858 thisjob->changed = 1;
3859
3860 if (thisjob->state != state) {
3861 TRACE(("Job %d: changing state from %d to %d\n",
3862 jobno(thisjob), thisjob->state, state));
3863 thisjob->state = state;
3864#if JOBS
3865 if (state == JOBSTOPPED) {
3866 set_curjob(thisjob, CUR_STOPPED);
3867 }
3868#endif
3869 }
3870 }
3871
3872 out:
3873 INT_ON;
3874
3875 if (thisjob && thisjob == job) {
3876 char s[48 + 1];
3877 int len;
3878
3879 len = sprint_status(s, status, 1);
3880 if (len) {
3881 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003882 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883 out2str(s);
3884 }
3885 }
3886 return pid;
3887}
3888
3889#if JOBS
3890static void
3891showjob(FILE *out, struct job *jp, int mode)
3892{
3893 struct procstat *ps;
3894 struct procstat *psend;
3895 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003896 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003897 char s[80];
3898
3899 ps = jp->ps;
3900
3901 if (mode & SHOW_PGID) {
3902 /* just output process (group) id of pipeline */
3903 fprintf(out, "%d\n", ps->pid);
3904 return;
3905 }
3906
3907 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003908 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003909
3910 if (jp == curjob)
3911 s[col - 2] = '+';
3912 else if (curjob && jp == curjob->prev_job)
3913 s[col - 2] = '-';
3914
3915 if (mode & SHOW_PID)
3916 col += fmtstr(s + col, 16, "%d ", ps->pid);
3917
3918 psend = ps + jp->nprocs;
3919
3920 if (jp->state == JOBRUNNING) {
3921 strcpy(s + col, "Running");
3922 col += sizeof("Running") - 1;
3923 } else {
3924 int status = psend[-1].status;
3925 if (jp->state == JOBSTOPPED)
3926 status = jp->stopstatus;
3927 col += sprint_status(s + col, status, 0);
3928 }
3929
3930 goto start;
3931
3932 do {
3933 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003934 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003935 start:
3936 fprintf(out, "%s%*c%s",
3937 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3938 );
3939 if (!(mode & SHOW_PID)) {
3940 showpipe(jp, out);
3941 break;
3942 }
3943 if (++ps == psend) {
3944 outcslow('\n', out);
3945 break;
3946 }
3947 } while (1);
3948
3949 jp->changed = 0;
3950
3951 if (jp->state == JOBDONE) {
3952 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3953 freejob(jp);
3954 }
3955}
3956
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957/*
3958 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3959 * statuses have changed since the last call to showjobs.
3960 */
3961static void
3962showjobs(FILE *out, int mode)
3963{
3964 struct job *jp;
3965
3966 TRACE(("showjobs(%x) called\n", mode));
3967
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003968 /* If not even one job changed, there is nothing to do */
3969 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 continue;
3971
3972 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003973 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003975 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 }
3977}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003978
3979static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00003980jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003981{
3982 int mode, m;
3983
3984 mode = 0;
3985 while ((m = nextopt("lp"))) {
3986 if (m == 'l')
3987 mode = SHOW_PID;
3988 else
3989 mode = SHOW_PGID;
3990 }
3991
3992 argv = argptr;
3993 if (*argv) {
3994 do
3995 showjob(stdout, getjob(*argv,0), mode);
3996 while (*++argv);
3997 } else
3998 showjobs(stdout, mode);
3999
4000 return 0;
4001}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002#endif /* JOBS */
4003
4004static int
4005getstatus(struct job *job)
4006{
4007 int status;
4008 int retval;
4009
4010 status = job->ps[job->nprocs - 1].status;
4011 retval = WEXITSTATUS(status);
4012 if (!WIFEXITED(status)) {
4013#if JOBS
4014 retval = WSTOPSIG(status);
4015 if (!WIFSTOPPED(status))
4016#endif
4017 {
4018 /* XXX: limits number of signals */
4019 retval = WTERMSIG(status);
4020#if JOBS
4021 if (retval == SIGINT)
4022 job->sigint = 1;
4023#endif
4024 }
4025 retval += 128;
4026 }
4027 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4028 jobno(job), job->nprocs, status, retval));
4029 return retval;
4030}
4031
4032static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00004033waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034{
4035 struct job *job;
4036 int retval;
4037 struct job *jp;
4038
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004039// exsig++;
4040// xbarrier();
4041 if (pendingsig)
4042 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043
4044 nextopt(nullstr);
4045 retval = 0;
4046
4047 argv = argptr;
4048 if (!*argv) {
4049 /* wait for all jobs */
4050 for (;;) {
4051 jp = curjob;
4052 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004053 if (!jp) /* no running procs */
4054 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004055 if (jp->state == JOBRUNNING)
4056 break;
4057 jp->waited = 1;
4058 jp = jp->prev_job;
4059 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004060 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 }
4062 }
4063
4064 retval = 127;
4065 do {
4066 if (**argv != '%') {
4067 pid_t pid = number(*argv);
4068 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004069 while (1) {
4070 if (!job)
4071 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004072 if (job->ps[job->nprocs - 1].pid == pid)
4073 break;
4074 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004075 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076 } else
4077 job = getjob(*argv, 0);
4078 /* loop until process terminated or stopped */
4079 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004080 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 job->waited = 1;
4082 retval = getstatus(job);
4083 repeat:
4084 ;
4085 } while (*++argv);
4086
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004087 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 return retval;
4089}
4090
4091static struct job *
4092growjobtab(void)
4093{
4094 size_t len;
4095 ptrdiff_t offset;
4096 struct job *jp, *jq;
4097
4098 len = njobs * sizeof(*jp);
4099 jq = jobtab;
4100 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4101
4102 offset = (char *)jp - (char *)jq;
4103 if (offset) {
4104 /* Relocate pointers */
4105 size_t l = len;
4106
4107 jq = (struct job *)((char *)jq + l);
4108 while (l) {
4109 l -= sizeof(*jp);
4110 jq--;
4111#define joff(p) ((struct job *)((char *)(p) + l))
4112#define jmove(p) (p) = (void *)((char *)(p) + offset)
4113 if (joff(jp)->ps == &jq->ps0)
4114 jmove(joff(jp)->ps);
4115 if (joff(jp)->prev_job)
4116 jmove(joff(jp)->prev_job);
4117 }
4118 if (curjob)
4119 jmove(curjob);
4120#undef joff
4121#undef jmove
4122 }
4123
4124 njobs += 4;
4125 jobtab = jp;
4126 jp = (struct job *)((char *)jp + len);
4127 jq = jp + 3;
4128 do {
4129 jq->used = 0;
4130 } while (--jq >= jp);
4131 return jp;
4132}
4133
4134/*
4135 * Return a new job structure.
4136 * Called with interrupts off.
4137 */
4138static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004139makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140{
4141 int i;
4142 struct job *jp;
4143
4144 for (i = njobs, jp = jobtab; ; jp++) {
4145 if (--i < 0) {
4146 jp = growjobtab();
4147 break;
4148 }
4149 if (jp->used == 0)
4150 break;
4151 if (jp->state != JOBDONE || !jp->waited)
4152 continue;
4153#if JOBS
4154 if (jobctl)
4155 continue;
4156#endif
4157 freejob(jp);
4158 break;
4159 }
4160 memset(jp, 0, sizeof(*jp));
4161#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004162 /* jp->jobctl is a bitfield.
4163 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164 if (jobctl)
4165 jp->jobctl = 1;
4166#endif
4167 jp->prev_job = curjob;
4168 curjob = jp;
4169 jp->used = 1;
4170 jp->ps = &jp->ps0;
4171 if (nprocs > 1) {
4172 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4173 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004174 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175 jobno(jp)));
4176 return jp;
4177}
4178
4179#if JOBS
4180/*
4181 * Return a string identifying a command (to be printed by the
4182 * jobs command).
4183 */
4184static char *cmdnextc;
4185
4186static void
4187cmdputs(const char *s)
4188{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004189 static const char vstype[VSTYPE + 1][3] = {
4190 "", "}", "-", "+", "?", "=",
4191 "%", "%%", "#", "##"
4192 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4193 };
4194
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 const char *p, *str;
4196 char c, cc[2] = " ";
4197 char *nextc;
4198 int subtype = 0;
4199 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004200
4201 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4202 p = s;
4203 while ((c = *p++) != 0) {
4204 str = 0;
4205 switch (c) {
4206 case CTLESC:
4207 c = *p++;
4208 break;
4209 case CTLVAR:
4210 subtype = *p++;
4211 if ((subtype & VSTYPE) == VSLENGTH)
4212 str = "${#";
4213 else
4214 str = "${";
4215 if (!(subtype & VSQUOTE) == !(quoted & 1))
4216 goto dostr;
4217 quoted ^= 1;
4218 c = '"';
4219 break;
4220 case CTLENDVAR:
4221 str = "\"}" + !(quoted & 1);
4222 quoted >>= 1;
4223 subtype = 0;
4224 goto dostr;
4225 case CTLBACKQ:
4226 str = "$(...)";
4227 goto dostr;
4228 case CTLBACKQ+CTLQUOTE:
4229 str = "\"$(...)\"";
4230 goto dostr;
4231#if ENABLE_ASH_MATH_SUPPORT
4232 case CTLARI:
4233 str = "$((";
4234 goto dostr;
4235 case CTLENDARI:
4236 str = "))";
4237 goto dostr;
4238#endif
4239 case CTLQUOTEMARK:
4240 quoted ^= 1;
4241 c = '"';
4242 break;
4243 case '=':
4244 if (subtype == 0)
4245 break;
4246 if ((subtype & VSTYPE) != VSNORMAL)
4247 quoted <<= 1;
4248 str = vstype[subtype & VSTYPE];
4249 if (subtype & VSNUL)
4250 c = ':';
4251 else
4252 goto checkstr;
4253 break;
4254 case '\'':
4255 case '\\':
4256 case '"':
4257 case '$':
4258 /* These can only happen inside quotes */
4259 cc[0] = c;
4260 str = cc;
4261 c = '\\';
4262 break;
4263 default:
4264 break;
4265 }
4266 USTPUTC(c, nextc);
4267 checkstr:
4268 if (!str)
4269 continue;
4270 dostr:
4271 while ((c = *str++)) {
4272 USTPUTC(c, nextc);
4273 }
4274 }
4275 if (quoted & 1) {
4276 USTPUTC('"', nextc);
4277 }
4278 *nextc = 0;
4279 cmdnextc = nextc;
4280}
4281
4282/* cmdtxt() and cmdlist() call each other */
4283static void cmdtxt(union node *n);
4284
4285static void
4286cmdlist(union node *np, int sep)
4287{
4288 for (; np; np = np->narg.next) {
4289 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004290 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004291 cmdtxt(np);
4292 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004293 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004294 }
4295}
4296
4297static void
4298cmdtxt(union node *n)
4299{
4300 union node *np;
4301 struct nodelist *lp;
4302 const char *p;
4303 char s[2];
4304
4305 if (!n)
4306 return;
4307 switch (n->type) {
4308 default:
4309#if DEBUG
4310 abort();
4311#endif
4312 case NPIPE:
4313 lp = n->npipe.cmdlist;
4314 for (;;) {
4315 cmdtxt(lp->n);
4316 lp = lp->next;
4317 if (!lp)
4318 break;
4319 cmdputs(" | ");
4320 }
4321 break;
4322 case NSEMI:
4323 p = "; ";
4324 goto binop;
4325 case NAND:
4326 p = " && ";
4327 goto binop;
4328 case NOR:
4329 p = " || ";
4330 binop:
4331 cmdtxt(n->nbinary.ch1);
4332 cmdputs(p);
4333 n = n->nbinary.ch2;
4334 goto donode;
4335 case NREDIR:
4336 case NBACKGND:
4337 n = n->nredir.n;
4338 goto donode;
4339 case NNOT:
4340 cmdputs("!");
4341 n = n->nnot.com;
4342 donode:
4343 cmdtxt(n);
4344 break;
4345 case NIF:
4346 cmdputs("if ");
4347 cmdtxt(n->nif.test);
4348 cmdputs("; then ");
4349 n = n->nif.ifpart;
4350 if (n->nif.elsepart) {
4351 cmdtxt(n);
4352 cmdputs("; else ");
4353 n = n->nif.elsepart;
4354 }
4355 p = "; fi";
4356 goto dotail;
4357 case NSUBSHELL:
4358 cmdputs("(");
4359 n = n->nredir.n;
4360 p = ")";
4361 goto dotail;
4362 case NWHILE:
4363 p = "while ";
4364 goto until;
4365 case NUNTIL:
4366 p = "until ";
4367 until:
4368 cmdputs(p);
4369 cmdtxt(n->nbinary.ch1);
4370 n = n->nbinary.ch2;
4371 p = "; done";
4372 dodo:
4373 cmdputs("; do ");
4374 dotail:
4375 cmdtxt(n);
4376 goto dotail2;
4377 case NFOR:
4378 cmdputs("for ");
4379 cmdputs(n->nfor.var);
4380 cmdputs(" in ");
4381 cmdlist(n->nfor.args, 1);
4382 n = n->nfor.body;
4383 p = "; done";
4384 goto dodo;
4385 case NDEFUN:
4386 cmdputs(n->narg.text);
4387 p = "() { ... }";
4388 goto dotail2;
4389 case NCMD:
4390 cmdlist(n->ncmd.args, 1);
4391 cmdlist(n->ncmd.redirect, 0);
4392 break;
4393 case NARG:
4394 p = n->narg.text;
4395 dotail2:
4396 cmdputs(p);
4397 break;
4398 case NHERE:
4399 case NXHERE:
4400 p = "<<...";
4401 goto dotail2;
4402 case NCASE:
4403 cmdputs("case ");
4404 cmdputs(n->ncase.expr->narg.text);
4405 cmdputs(" in ");
4406 for (np = n->ncase.cases; np; np = np->nclist.next) {
4407 cmdtxt(np->nclist.pattern);
4408 cmdputs(") ");
4409 cmdtxt(np->nclist.body);
4410 cmdputs(";; ");
4411 }
4412 p = "esac";
4413 goto dotail2;
4414 case NTO:
4415 p = ">";
4416 goto redir;
4417 case NCLOBBER:
4418 p = ">|";
4419 goto redir;
4420 case NAPPEND:
4421 p = ">>";
4422 goto redir;
4423 case NTOFD:
4424 p = ">&";
4425 goto redir;
4426 case NFROM:
4427 p = "<";
4428 goto redir;
4429 case NFROMFD:
4430 p = "<&";
4431 goto redir;
4432 case NFROMTO:
4433 p = "<>";
4434 redir:
4435 s[0] = n->nfile.fd + '0';
4436 s[1] = '\0';
4437 cmdputs(s);
4438 cmdputs(p);
4439 if (n->type == NTOFD || n->type == NFROMFD) {
4440 s[0] = n->ndup.dupfd + '0';
4441 p = s;
4442 goto dotail2;
4443 }
4444 n = n->nfile.fname;
4445 goto donode;
4446 }
4447}
4448
4449static char *
4450commandtext(union node *n)
4451{
4452 char *name;
4453
4454 STARTSTACKSTR(cmdnextc);
4455 cmdtxt(n);
4456 name = stackblock();
4457 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4458 name, cmdnextc, cmdnextc));
4459 return ckstrdup(name);
4460}
4461#endif /* JOBS */
4462
4463/*
4464 * Fork off a subshell. If we are doing job control, give the subshell its
4465 * own process group. Jp is a job structure that the job is to be added to.
4466 * N is the command that will be evaluated by the child. Both jp and n may
4467 * be NULL. The mode parameter can be one of the following:
4468 * FORK_FG - Fork off a foreground process.
4469 * FORK_BG - Fork off a background process.
4470 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4471 * process group even if job control is on.
4472 *
4473 * When job control is turned off, background processes have their standard
4474 * input redirected to /dev/null (except for the second and later processes
4475 * in a pipeline).
4476 *
4477 * Called with interrupts off.
4478 */
4479/*
4480 * Clear traps on a fork.
4481 */
4482static void
4483clear_traps(void)
4484{
4485 char **tp;
4486
4487 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004488 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 INT_OFF;
4490 free(*tp);
4491 *tp = NULL;
4492 if (tp != &trap[0])
4493 setsignal(tp - trap);
4494 INT_ON;
4495 }
4496 }
4497}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004498
4499/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004501
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004502/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004504forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505{
4506 int oldlvl;
4507
4508 TRACE(("Child shell %d\n", getpid()));
4509 oldlvl = shlvl;
4510 shlvl++;
4511
4512 closescript();
4513 clear_traps();
4514#if JOBS
4515 /* do job control only in root shell */
4516 jobctl = 0;
4517 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4518 pid_t pgrp;
4519
4520 if (jp->nprocs == 0)
4521 pgrp = getpid();
4522 else
4523 pgrp = jp->ps[0].pid;
4524 /* This can fail because we are doing it in the parent also */
4525 (void)setpgid(0, pgrp);
4526 if (mode == FORK_FG)
4527 xtcsetpgrp(ttyfd, pgrp);
4528 setsignal(SIGTSTP);
4529 setsignal(SIGTTOU);
4530 } else
4531#endif
4532 if (mode == FORK_BG) {
4533 ignoresig(SIGINT);
4534 ignoresig(SIGQUIT);
4535 if (jp->nprocs == 0) {
4536 close(0);
4537 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004538 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004539 }
4540 }
4541 if (!oldlvl && iflag) {
4542 setsignal(SIGINT);
4543 setsignal(SIGQUIT);
4544 setsignal(SIGTERM);
4545 }
4546 for (jp = curjob; jp; jp = jp->prev_job)
4547 freejob(jp);
4548 jobless = 0;
4549}
4550
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004551/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004552#if !JOBS
4553#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4554#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004555static void
4556forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4557{
4558 TRACE(("In parent shell: child = %d\n", pid));
4559 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004560 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4561 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004562 jobless++;
4563 return;
4564 }
4565#if JOBS
4566 if (mode != FORK_NOJOB && jp->jobctl) {
4567 int pgrp;
4568
4569 if (jp->nprocs == 0)
4570 pgrp = pid;
4571 else
4572 pgrp = jp->ps[0].pid;
4573 /* This can fail because we are doing it in the child also */
4574 setpgid(pid, pgrp);
4575 }
4576#endif
4577 if (mode == FORK_BG) {
4578 backgndpid = pid; /* set $! */
4579 set_curjob(jp, CUR_RUNNING);
4580 }
4581 if (jp) {
4582 struct procstat *ps = &jp->ps[jp->nprocs++];
4583 ps->pid = pid;
4584 ps->status = -1;
4585 ps->cmd = nullstr;
4586#if JOBS
4587 if (jobctl && n)
4588 ps->cmd = commandtext(n);
4589#endif
4590 }
4591}
4592
4593static int
4594forkshell(struct job *jp, union node *n, int mode)
4595{
4596 int pid;
4597
4598 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4599 pid = fork();
4600 if (pid < 0) {
4601 TRACE(("Fork failed, errno=%d", errno));
4602 if (jp)
4603 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004604 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004605 }
4606 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004607 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004608 else
4609 forkparent(jp, n, mode, pid);
4610 return pid;
4611}
4612
4613/*
4614 * Wait for job to finish.
4615 *
4616 * Under job control we have the problem that while a child process is
4617 * running interrupts generated by the user are sent to the child but not
4618 * to the shell. This means that an infinite loop started by an inter-
4619 * active user may be hard to kill. With job control turned off, an
4620 * interactive user may place an interactive program inside a loop. If
4621 * the interactive program catches interrupts, the user doesn't want
4622 * these interrupts to also abort the loop. The approach we take here
4623 * is to have the shell ignore interrupt signals while waiting for a
4624 * foreground process to terminate, and then send itself an interrupt
4625 * signal if the child process was terminated by an interrupt signal.
4626 * Unfortunately, some programs want to do a bit of cleanup and then
4627 * exit on interrupt; unless these processes terminate themselves by
4628 * sending a signal to themselves (instead of calling exit) they will
4629 * confuse this approach.
4630 *
4631 * Called with interrupts off.
4632 */
4633static int
4634waitforjob(struct job *jp)
4635{
4636 int st;
4637
4638 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4639 while (jp->state == JOBRUNNING) {
4640 dowait(DOWAIT_BLOCK, jp);
4641 }
4642 st = getstatus(jp);
4643#if JOBS
4644 if (jp->jobctl) {
4645 xtcsetpgrp(ttyfd, rootpid);
4646 /*
4647 * This is truly gross.
4648 * If we're doing job control, then we did a TIOCSPGRP which
4649 * caused us (the shell) to no longer be in the controlling
4650 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4651 * intuit from the subprocess exit status whether a SIGINT
4652 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4653 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004654 if (jp->sigint) /* TODO: do the same with all signals */
4655 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656 }
4657 if (jp->state == JOBDONE)
4658#endif
4659 freejob(jp);
4660 return st;
4661}
4662
4663/*
4664 * return 1 if there are stopped jobs, otherwise 0
4665 */
4666static int
4667stoppedjobs(void)
4668{
4669 struct job *jp;
4670 int retval;
4671
4672 retval = 0;
4673 if (job_warning)
4674 goto out;
4675 jp = curjob;
4676 if (jp && jp->state == JOBSTOPPED) {
4677 out2str("You have stopped jobs.\n");
4678 job_warning = 2;
4679 retval++;
4680 }
4681 out:
4682 return retval;
4683}
4684
4685
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004686/* ============ redir.c
4687 *
4688 * Code for dealing with input/output redirection.
4689 */
4690
4691#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004692#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004693#ifndef PIPE_BUF
4694# define PIPESIZE 4096 /* amount of buffering in a pipe */
4695#else
4696# define PIPESIZE PIPE_BUF
4697#endif
4698
4699/*
4700 * Open a file in noclobber mode.
4701 * The code was copied from bash.
4702 */
4703static int
4704noclobberopen(const char *fname)
4705{
4706 int r, fd;
4707 struct stat finfo, finfo2;
4708
4709 /*
4710 * If the file exists and is a regular file, return an error
4711 * immediately.
4712 */
4713 r = stat(fname, &finfo);
4714 if (r == 0 && S_ISREG(finfo.st_mode)) {
4715 errno = EEXIST;
4716 return -1;
4717 }
4718
4719 /*
4720 * If the file was not present (r != 0), make sure we open it
4721 * exclusively so that if it is created before we open it, our open
4722 * will fail. Make sure that we do not truncate an existing file.
4723 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4724 * file was not a regular file, we leave O_EXCL off.
4725 */
4726 if (r != 0)
4727 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4728 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4729
4730 /* If the open failed, return the file descriptor right away. */
4731 if (fd < 0)
4732 return fd;
4733
4734 /*
4735 * OK, the open succeeded, but the file may have been changed from a
4736 * non-regular file to a regular file between the stat and the open.
4737 * We are assuming that the O_EXCL open handles the case where FILENAME
4738 * did not exist and is symlinked to an existing file between the stat
4739 * and open.
4740 */
4741
4742 /*
4743 * If we can open it and fstat the file descriptor, and neither check
4744 * revealed that it was a regular file, and the file has not been
4745 * replaced, return the file descriptor.
4746 */
4747 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4748 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4749 return fd;
4750
4751 /* The file has been replaced. badness. */
4752 close(fd);
4753 errno = EEXIST;
4754 return -1;
4755}
4756
4757/*
4758 * Handle here documents. Normally we fork off a process to write the
4759 * data to a pipe. If the document is short, we can stuff the data in
4760 * the pipe without forking.
4761 */
4762/* openhere needs this forward reference */
4763static void expandhere(union node *arg, int fd);
4764static int
4765openhere(union node *redir)
4766{
4767 int pip[2];
4768 size_t len = 0;
4769
4770 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004771 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772 if (redir->type == NHERE) {
4773 len = strlen(redir->nhere.doc->narg.text);
4774 if (len <= PIPESIZE) {
4775 full_write(pip[1], redir->nhere.doc->narg.text, len);
4776 goto out;
4777 }
4778 }
4779 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4780 close(pip[0]);
4781 signal(SIGINT, SIG_IGN);
4782 signal(SIGQUIT, SIG_IGN);
4783 signal(SIGHUP, SIG_IGN);
4784#ifdef SIGTSTP
4785 signal(SIGTSTP, SIG_IGN);
4786#endif
4787 signal(SIGPIPE, SIG_DFL);
4788 if (redir->type == NHERE)
4789 full_write(pip[1], redir->nhere.doc->narg.text, len);
4790 else
4791 expandhere(redir->nhere.doc, pip[1]);
4792 _exit(0);
4793 }
4794 out:
4795 close(pip[1]);
4796 return pip[0];
4797}
4798
4799static int
4800openredirect(union node *redir)
4801{
4802 char *fname;
4803 int f;
4804
4805 switch (redir->nfile.type) {
4806 case NFROM:
4807 fname = redir->nfile.expfname;
4808 f = open(fname, O_RDONLY);
4809 if (f < 0)
4810 goto eopen;
4811 break;
4812 case NFROMTO:
4813 fname = redir->nfile.expfname;
4814 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4815 if (f < 0)
4816 goto ecreate;
4817 break;
4818 case NTO:
4819 /* Take care of noclobber mode. */
4820 if (Cflag) {
4821 fname = redir->nfile.expfname;
4822 f = noclobberopen(fname);
4823 if (f < 0)
4824 goto ecreate;
4825 break;
4826 }
4827 /* FALLTHROUGH */
4828 case NCLOBBER:
4829 fname = redir->nfile.expfname;
4830 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4831 if (f < 0)
4832 goto ecreate;
4833 break;
4834 case NAPPEND:
4835 fname = redir->nfile.expfname;
4836 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4837 if (f < 0)
4838 goto ecreate;
4839 break;
4840 default:
4841#if DEBUG
4842 abort();
4843#endif
4844 /* Fall through to eliminate warning. */
4845 case NTOFD:
4846 case NFROMFD:
4847 f = -1;
4848 break;
4849 case NHERE:
4850 case NXHERE:
4851 f = openhere(redir);
4852 break;
4853 }
4854
4855 return f;
4856 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004857 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004858 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004859 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004860}
4861
4862/*
4863 * Copy a file descriptor to be >= to. Returns -1
4864 * if the source file descriptor is closed, EMPTY if there are no unused
4865 * file descriptors left.
4866 */
4867static int
4868copyfd(int from, int to)
4869{
4870 int newfd;
4871
4872 newfd = fcntl(from, F_DUPFD, to);
4873 if (newfd < 0) {
4874 if (errno == EMFILE)
4875 return EMPTY;
4876 ash_msg_and_raise_error("%d: %m", from);
4877 }
4878 return newfd;
4879}
4880
4881static void
4882dupredirect(union node *redir, int f)
4883{
4884 int fd = redir->nfile.fd;
4885
4886 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4887 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4888 copyfd(redir->ndup.dupfd, fd);
4889 }
4890 return;
4891 }
4892
4893 if (f != fd) {
4894 copyfd(f, fd);
4895 close(f);
4896 }
4897}
4898
4899/*
4900 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4901 * old file descriptors are stashed away so that the redirection can be
4902 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4903 * standard output, and the standard error if it becomes a duplicate of
4904 * stdout, is saved in memory.
4905 */
4906/* flags passed to redirect */
4907#define REDIR_PUSH 01 /* save previous values of file descriptors */
4908#define REDIR_SAVEFD2 03 /* set preverrout */
4909static void
4910redirect(union node *redir, int flags)
4911{
4912 union node *n;
4913 struct redirtab *sv;
4914 int i;
4915 int fd;
4916 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004917
Denis Vlasenko01631112007-12-16 17:20:38 +00004918 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919 if (!redir) {
4920 return;
4921 }
4922 sv = NULL;
4923 INT_OFF;
4924 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004925 sv = ckmalloc(sizeof(*sv));
4926 sv->next = redirlist;
4927 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004928 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004929 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004930 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004931 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004932 }
4933 n = redir;
4934 do {
4935 fd = n->nfile.fd;
4936 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4937 && n->ndup.dupfd == fd)
4938 continue; /* redirect from/to same file descriptor */
4939
4940 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004941 if (fd == newfd) {
4942 /* Descriptor wasn't open before redirect.
4943 * Mark it for close in the future */
4944 if (sv && sv->renamed[fd] == EMPTY)
4945 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004946 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004947 }
4948 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004949 i = fcntl(fd, F_DUPFD, 10);
4950
4951 if (i == -1) {
4952 i = errno;
4953 if (i != EBADF) {
4954 close(newfd);
4955 errno = i;
4956 ash_msg_and_raise_error("%d: %m", fd);
4957 /* NOTREACHED */
4958 }
4959 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004960 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004961 close(fd);
4962 }
4963 } else {
4964 close(fd);
4965 }
4966 dupredirect(n, newfd);
4967 } while ((n = n->nfile.next));
4968 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004969 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004970 preverrout_fd = sv->renamed[2];
4971}
4972
4973/*
4974 * Undo the effects of the last redirection.
4975 */
4976static void
4977popredir(int drop)
4978{
4979 struct redirtab *rp;
4980 int i;
4981
Denis Vlasenko01631112007-12-16 17:20:38 +00004982 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004983 return;
4984 INT_OFF;
4985 rp = redirlist;
4986 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004987 if (rp->renamed[i] == CLOSED) {
4988 if (!drop)
4989 close(i);
4990 continue;
4991 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004992 if (rp->renamed[i] != EMPTY) {
4993 if (!drop) {
4994 close(i);
4995 copyfd(rp->renamed[i], i);
4996 }
4997 close(rp->renamed[i]);
4998 }
4999 }
5000 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005001 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005002 free(rp);
5003 INT_ON;
5004}
5005
5006/*
5007 * Undo all redirections. Called on error or interrupt.
5008 */
5009
5010/*
5011 * Discard all saved file descriptors.
5012 */
5013static void
5014clearredir(int drop)
5015{
5016 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005017 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005018 if (!redirlist)
5019 break;
5020 popredir(drop);
5021 }
5022}
5023
5024static int
5025redirectsafe(union node *redir, int flags)
5026{
5027 int err;
5028 volatile int saveint;
5029 struct jmploc *volatile savehandler = exception_handler;
5030 struct jmploc jmploc;
5031
5032 SAVE_INT(saveint);
5033 err = setjmp(jmploc.loc) * 2;
5034 if (!err) {
5035 exception_handler = &jmploc;
5036 redirect(redir, flags);
5037 }
5038 exception_handler = savehandler;
5039 if (err && exception != EXERROR)
5040 longjmp(exception_handler->loc, 1);
5041 RESTORE_INT(saveint);
5042 return err;
5043}
5044
5045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005046/* ============ Routines to expand arguments to commands
5047 *
5048 * We have to deal with backquotes, shell variables, and file metacharacters.
5049 */
5050
5051/*
5052 * expandarg flags
5053 */
5054#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5055#define EXP_TILDE 0x2 /* do normal tilde expansion */
5056#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5057#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5058#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5059#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5060#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5061#define EXP_WORD 0x80 /* expand word in parameter expansion */
5062#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5063/*
5064 * _rmescape() flags
5065 */
5066#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5067#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5068#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5069#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5070#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5071
5072/*
5073 * Structure specifying which parts of the string should be searched
5074 * for IFS characters.
5075 */
5076struct ifsregion {
5077 struct ifsregion *next; /* next region in list */
5078 int begoff; /* offset of start of region */
5079 int endoff; /* offset of end of region */
5080 int nulonly; /* search for nul bytes only */
5081};
5082
5083struct arglist {
5084 struct strlist *list;
5085 struct strlist **lastp;
5086};
5087
5088/* output of current string */
5089static char *expdest;
5090/* list of back quote expressions */
5091static struct nodelist *argbackq;
5092/* first struct in list of ifs regions */
5093static struct ifsregion ifsfirst;
5094/* last struct in list */
5095static struct ifsregion *ifslastp;
5096/* holds expanded arg list */
5097static struct arglist exparg;
5098
5099/*
5100 * Our own itoa().
5101 */
5102static int
5103cvtnum(arith_t num)
5104{
5105 int len;
5106
5107 expdest = makestrspace(32, expdest);
5108#if ENABLE_ASH_MATH_SUPPORT_64
5109 len = fmtstr(expdest, 32, "%lld", (long long) num);
5110#else
5111 len = fmtstr(expdest, 32, "%ld", num);
5112#endif
5113 STADJUST(len, expdest);
5114 return len;
5115}
5116
5117static size_t
5118esclen(const char *start, const char *p)
5119{
5120 size_t esc = 0;
5121
5122 while (p > start && *--p == CTLESC) {
5123 esc++;
5124 }
5125 return esc;
5126}
5127
5128/*
5129 * Remove any CTLESC characters from a string.
5130 */
5131static char *
5132_rmescapes(char *str, int flag)
5133{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005134 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005135
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005136 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005137 unsigned inquotes;
5138 int notescaped;
5139 int globbing;
5140
5141 p = strpbrk(str, qchars);
5142 if (!p) {
5143 return str;
5144 }
5145 q = p;
5146 r = str;
5147 if (flag & RMESCAPE_ALLOC) {
5148 size_t len = p - str;
5149 size_t fulllen = len + strlen(p) + 1;
5150
5151 if (flag & RMESCAPE_GROW) {
5152 r = makestrspace(fulllen, expdest);
5153 } else if (flag & RMESCAPE_HEAP) {
5154 r = ckmalloc(fulllen);
5155 } else {
5156 r = stalloc(fulllen);
5157 }
5158 q = r;
5159 if (len > 0) {
5160 q = memcpy(q, str, len) + len;
5161 }
5162 }
5163 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5164 globbing = flag & RMESCAPE_GLOB;
5165 notescaped = globbing;
5166 while (*p) {
5167 if (*p == CTLQUOTEMARK) {
5168 inquotes = ~inquotes;
5169 p++;
5170 notescaped = globbing;
5171 continue;
5172 }
5173 if (*p == '\\') {
5174 /* naked back slash */
5175 notescaped = 0;
5176 goto copy;
5177 }
5178 if (*p == CTLESC) {
5179 p++;
5180 if (notescaped && inquotes && *p != '/') {
5181 *q++ = '\\';
5182 }
5183 }
5184 notescaped = globbing;
5185 copy:
5186 *q++ = *p++;
5187 }
5188 *q = '\0';
5189 if (flag & RMESCAPE_GROW) {
5190 expdest = r;
5191 STADJUST(q - r + 1, expdest);
5192 }
5193 return r;
5194}
5195#define rmescapes(p) _rmescapes((p), 0)
5196
5197#define pmatch(a, b) !fnmatch((a), (b), 0)
5198
5199/*
5200 * Prepare a pattern for a expmeta (internal glob(3)) call.
5201 *
5202 * Returns an stalloced string.
5203 */
5204static char *
5205preglob(const char *pattern, int quoted, int flag)
5206{
5207 flag |= RMESCAPE_GLOB;
5208 if (quoted) {
5209 flag |= RMESCAPE_QUOTED;
5210 }
5211 return _rmescapes((char *)pattern, flag);
5212}
5213
5214/*
5215 * Put a string on the stack.
5216 */
5217static void
5218memtodest(const char *p, size_t len, int syntax, int quotes)
5219{
5220 char *q = expdest;
5221
5222 q = makestrspace(len * 2, q);
5223
5224 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005225 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005226 if (!c)
5227 continue;
5228 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5229 USTPUTC(CTLESC, q);
5230 USTPUTC(c, q);
5231 }
5232
5233 expdest = q;
5234}
5235
5236static void
5237strtodest(const char *p, int syntax, int quotes)
5238{
5239 memtodest(p, strlen(p), syntax, quotes);
5240}
5241
5242/*
5243 * Record the fact that we have to scan this region of the
5244 * string for IFS characters.
5245 */
5246static void
5247recordregion(int start, int end, int nulonly)
5248{
5249 struct ifsregion *ifsp;
5250
5251 if (ifslastp == NULL) {
5252 ifsp = &ifsfirst;
5253 } else {
5254 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005255 ifsp = ckzalloc(sizeof(*ifsp));
5256 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005257 ifslastp->next = ifsp;
5258 INT_ON;
5259 }
5260 ifslastp = ifsp;
5261 ifslastp->begoff = start;
5262 ifslastp->endoff = end;
5263 ifslastp->nulonly = nulonly;
5264}
5265
5266static void
5267removerecordregions(int endoff)
5268{
5269 if (ifslastp == NULL)
5270 return;
5271
5272 if (ifsfirst.endoff > endoff) {
5273 while (ifsfirst.next != NULL) {
5274 struct ifsregion *ifsp;
5275 INT_OFF;
5276 ifsp = ifsfirst.next->next;
5277 free(ifsfirst.next);
5278 ifsfirst.next = ifsp;
5279 INT_ON;
5280 }
5281 if (ifsfirst.begoff > endoff)
5282 ifslastp = NULL;
5283 else {
5284 ifslastp = &ifsfirst;
5285 ifsfirst.endoff = endoff;
5286 }
5287 return;
5288 }
5289
5290 ifslastp = &ifsfirst;
5291 while (ifslastp->next && ifslastp->next->begoff < endoff)
5292 ifslastp=ifslastp->next;
5293 while (ifslastp->next != NULL) {
5294 struct ifsregion *ifsp;
5295 INT_OFF;
5296 ifsp = ifslastp->next->next;
5297 free(ifslastp->next);
5298 ifslastp->next = ifsp;
5299 INT_ON;
5300 }
5301 if (ifslastp->endoff > endoff)
5302 ifslastp->endoff = endoff;
5303}
5304
5305static char *
5306exptilde(char *startp, char *p, int flag)
5307{
5308 char c;
5309 char *name;
5310 struct passwd *pw;
5311 const char *home;
5312 int quotes = flag & (EXP_FULL | EXP_CASE);
5313 int startloc;
5314
5315 name = p + 1;
5316
5317 while ((c = *++p) != '\0') {
5318 switch (c) {
5319 case CTLESC:
5320 return startp;
5321 case CTLQUOTEMARK:
5322 return startp;
5323 case ':':
5324 if (flag & EXP_VARTILDE)
5325 goto done;
5326 break;
5327 case '/':
5328 case CTLENDVAR:
5329 goto done;
5330 }
5331 }
5332 done:
5333 *p = '\0';
5334 if (*name == '\0') {
5335 home = lookupvar(homestr);
5336 } else {
5337 pw = getpwnam(name);
5338 if (pw == NULL)
5339 goto lose;
5340 home = pw->pw_dir;
5341 }
5342 if (!home || !*home)
5343 goto lose;
5344 *p = c;
5345 startloc = expdest - (char *)stackblock();
5346 strtodest(home, SQSYNTAX, quotes);
5347 recordregion(startloc, expdest - (char *)stackblock(), 0);
5348 return p;
5349 lose:
5350 *p = c;
5351 return startp;
5352}
5353
5354/*
5355 * Execute a command inside back quotes. If it's a builtin command, we
5356 * want to save its output in a block obtained from malloc. Otherwise
5357 * we fork off a subprocess and get the output of the command via a pipe.
5358 * Should be called with interrupts off.
5359 */
5360struct backcmd { /* result of evalbackcmd */
5361 int fd; /* file descriptor to read from */
5362 char *buf; /* buffer */
5363 int nleft; /* number of chars in buffer */
5364 struct job *jp; /* job structure for command */
5365};
5366
5367/* These forward decls are needed to use "eval" code for backticks handling: */
5368static int back_exitstatus; /* exit status of backquoted command */
5369#define EV_EXIT 01 /* exit after evaluating tree */
5370static void evaltree(union node *, int);
5371
5372static void
5373evalbackcmd(union node *n, struct backcmd *result)
5374{
5375 int saveherefd;
5376
5377 result->fd = -1;
5378 result->buf = NULL;
5379 result->nleft = 0;
5380 result->jp = NULL;
5381 if (n == NULL) {
5382 goto out;
5383 }
5384
5385 saveherefd = herefd;
5386 herefd = -1;
5387
5388 {
5389 int pip[2];
5390 struct job *jp;
5391
5392 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005393 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005394 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005395 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5396 FORCE_INT_ON;
5397 close(pip[0]);
5398 if (pip[1] != 1) {
5399 close(1);
5400 copyfd(pip[1], 1);
5401 close(pip[1]);
5402 }
5403 eflag = 0;
5404 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5405 /* NOTREACHED */
5406 }
5407 close(pip[1]);
5408 result->fd = pip[0];
5409 result->jp = jp;
5410 }
5411 herefd = saveherefd;
5412 out:
5413 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5414 result->fd, result->buf, result->nleft, result->jp));
5415}
5416
5417/*
5418 * Expand stuff in backwards quotes.
5419 */
5420static void
5421expbackq(union node *cmd, int quoted, int quotes)
5422{
5423 struct backcmd in;
5424 int i;
5425 char buf[128];
5426 char *p;
5427 char *dest;
5428 int startloc;
5429 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5430 struct stackmark smark;
5431
5432 INT_OFF;
5433 setstackmark(&smark);
5434 dest = expdest;
5435 startloc = dest - (char *)stackblock();
5436 grabstackstr(dest);
5437 evalbackcmd(cmd, &in);
5438 popstackmark(&smark);
5439
5440 p = in.buf;
5441 i = in.nleft;
5442 if (i == 0)
5443 goto read;
5444 for (;;) {
5445 memtodest(p, i, syntax, quotes);
5446 read:
5447 if (in.fd < 0)
5448 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005449 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005450 TRACE(("expbackq: read returns %d\n", i));
5451 if (i <= 0)
5452 break;
5453 p = buf;
5454 }
5455
Denis Vlasenko60818682007-09-28 22:07:23 +00005456 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005457 if (in.fd >= 0) {
5458 close(in.fd);
5459 back_exitstatus = waitforjob(in.jp);
5460 }
5461 INT_ON;
5462
5463 /* Eat all trailing newlines */
5464 dest = expdest;
5465 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5466 STUNPUTC(dest);
5467 expdest = dest;
5468
5469 if (quoted == 0)
5470 recordregion(startloc, dest - (char *)stackblock(), 0);
5471 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5472 (dest - (char *)stackblock()) - startloc,
5473 (dest - (char *)stackblock()) - startloc,
5474 stackblock() + startloc));
5475}
5476
5477#if ENABLE_ASH_MATH_SUPPORT
5478/*
5479 * Expand arithmetic expression. Backup to start of expression,
5480 * evaluate, place result in (backed up) result, adjust string position.
5481 */
5482static void
5483expari(int quotes)
5484{
5485 char *p, *start;
5486 int begoff;
5487 int flag;
5488 int len;
5489
5490 /* ifsfree(); */
5491
5492 /*
5493 * This routine is slightly over-complicated for
5494 * efficiency. Next we scan backwards looking for the
5495 * start of arithmetic.
5496 */
5497 start = stackblock();
5498 p = expdest - 1;
5499 *p = '\0';
5500 p--;
5501 do {
5502 int esc;
5503
5504 while (*p != CTLARI) {
5505 p--;
5506#if DEBUG
5507 if (p < start) {
5508 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5509 }
5510#endif
5511 }
5512
5513 esc = esclen(start, p);
5514 if (!(esc % 2)) {
5515 break;
5516 }
5517
5518 p -= esc + 1;
5519 } while (1);
5520
5521 begoff = p - start;
5522
5523 removerecordregions(begoff);
5524
5525 flag = p[1];
5526
5527 expdest = p;
5528
5529 if (quotes)
5530 rmescapes(p + 2);
5531
5532 len = cvtnum(dash_arith(p + 2));
5533
5534 if (flag != '"')
5535 recordregion(begoff, begoff + len, 0);
5536}
5537#endif
5538
5539/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005540static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541
5542/*
5543 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5544 * characters to allow for further processing. Otherwise treat
5545 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005546 *
5547 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5548 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5549 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005550 */
5551static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005552argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005554 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005555 '=',
5556 ':',
5557 CTLQUOTEMARK,
5558 CTLENDVAR,
5559 CTLESC,
5560 CTLVAR,
5561 CTLBACKQ,
5562 CTLBACKQ | CTLQUOTE,
5563#if ENABLE_ASH_MATH_SUPPORT
5564 CTLENDARI,
5565#endif
5566 0
5567 };
5568 const char *reject = spclchars;
5569 int c;
5570 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5571 int breakall = flag & EXP_WORD;
5572 int inquotes;
5573 size_t length;
5574 int startloc;
5575
5576 if (!(flag & EXP_VARTILDE)) {
5577 reject += 2;
5578 } else if (flag & EXP_VARTILDE2) {
5579 reject++;
5580 }
5581 inquotes = 0;
5582 length = 0;
5583 if (flag & EXP_TILDE) {
5584 char *q;
5585
5586 flag &= ~EXP_TILDE;
5587 tilde:
5588 q = p;
5589 if (*q == CTLESC && (flag & EXP_QWORD))
5590 q++;
5591 if (*q == '~')
5592 p = exptilde(p, q, flag);
5593 }
5594 start:
5595 startloc = expdest - (char *)stackblock();
5596 for (;;) {
5597 length += strcspn(p + length, reject);
5598 c = p[length];
5599 if (c && (!(c & 0x80)
5600#if ENABLE_ASH_MATH_SUPPORT
5601 || c == CTLENDARI
5602#endif
5603 )) {
5604 /* c == '=' || c == ':' || c == CTLENDARI */
5605 length++;
5606 }
5607 if (length > 0) {
5608 int newloc;
5609 expdest = stack_nputstr(p, length, expdest);
5610 newloc = expdest - (char *)stackblock();
5611 if (breakall && !inquotes && newloc > startloc) {
5612 recordregion(startloc, newloc, 0);
5613 }
5614 startloc = newloc;
5615 }
5616 p += length + 1;
5617 length = 0;
5618
5619 switch (c) {
5620 case '\0':
5621 goto breakloop;
5622 case '=':
5623 if (flag & EXP_VARTILDE2) {
5624 p--;
5625 continue;
5626 }
5627 flag |= EXP_VARTILDE2;
5628 reject++;
5629 /* fall through */
5630 case ':':
5631 /*
5632 * sort of a hack - expand tildes in variable
5633 * assignments (after the first '=' and after ':'s).
5634 */
5635 if (*--p == '~') {
5636 goto tilde;
5637 }
5638 continue;
5639 }
5640
5641 switch (c) {
5642 case CTLENDVAR: /* ??? */
5643 goto breakloop;
5644 case CTLQUOTEMARK:
5645 /* "$@" syntax adherence hack */
5646 if (
5647 !inquotes &&
5648 !memcmp(p, dolatstr, 4) &&
5649 (p[4] == CTLQUOTEMARK || (
5650 p[4] == CTLENDVAR &&
5651 p[5] == CTLQUOTEMARK
5652 ))
5653 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005654 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005655 goto start;
5656 }
5657 inquotes = !inquotes;
5658 addquote:
5659 if (quotes) {
5660 p--;
5661 length++;
5662 startloc++;
5663 }
5664 break;
5665 case CTLESC:
5666 startloc++;
5667 length++;
5668 goto addquote;
5669 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005670 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005671 goto start;
5672 case CTLBACKQ:
5673 c = 0;
5674 case CTLBACKQ|CTLQUOTE:
5675 expbackq(argbackq->n, c, quotes);
5676 argbackq = argbackq->next;
5677 goto start;
5678#if ENABLE_ASH_MATH_SUPPORT
5679 case CTLENDARI:
5680 p--;
5681 expari(quotes);
5682 goto start;
5683#endif
5684 }
5685 }
5686 breakloop:
5687 ;
5688}
5689
5690static char *
Denis Vlasenko68404f12008-03-17 09:00:54 +00005691scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005692 int zero)
5693{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005694 char *loc, *loc2, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005695 char c;
5696
5697 loc = startp;
5698 loc2 = rmesc;
5699 do {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005700 int match = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005701 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005702
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005703 c = *loc2;
5704 if (zero) {
5705 *loc2 = '\0';
5706 s = rmesc;
5707 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005708
5709 // chop off end if its '*'
5710 full = strrchr(str, '*');
5711 if (full && full != str)
5712 match--;
5713
5714 // If str starts with '*' replace with s.
5715 if ((*str == '*') && strlen(s) >= match) {
5716 full = xstrdup(s);
5717 strncpy(full+strlen(s)-match+1, str+1, match-1);
5718 } else
5719 full = xstrndup(str, match);
5720 match = strncmp(s, full, strlen(full));
5721 free(full);
5722
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723 *loc2 = c;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005724 if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005725 return loc;
5726 if (quotes && *loc == CTLESC)
5727 loc++;
5728 loc++;
5729 loc2++;
5730 } while (c);
5731 return 0;
5732}
5733
5734static char *
5735scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5736 int zero)
5737{
5738 int esc = 0;
5739 char *loc;
5740 char *loc2;
5741
5742 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5743 int match;
5744 char c = *loc2;
5745 const char *s = loc2;
5746 if (zero) {
5747 *loc2 = '\0';
5748 s = rmesc;
5749 }
5750 match = pmatch(str, s);
5751 *loc2 = c;
5752 if (match)
5753 return loc;
5754 loc--;
5755 if (quotes) {
5756 if (--esc < 0) {
5757 esc = esclen(startp, loc);
5758 }
5759 if (esc % 2) {
5760 esc--;
5761 loc--;
5762 }
5763 }
5764 }
5765 return 0;
5766}
5767
5768static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5769static void
5770varunset(const char *end, const char *var, const char *umsg, int varflags)
5771{
5772 const char *msg;
5773 const char *tail;
5774
5775 tail = nullstr;
5776 msg = "parameter not set";
5777 if (umsg) {
5778 if (*end == CTLENDVAR) {
5779 if (varflags & VSNUL)
5780 tail = " or null";
5781 } else
5782 msg = umsg;
5783 }
5784 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5785}
5786
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005787#if ENABLE_ASH_BASH_COMPAT
5788static char *
5789parse_sub_pattern(char *arg, int inquotes)
5790{
5791 char *idx, *repl = NULL;
5792 unsigned char c;
5793
5794 for (idx = arg; *arg; arg++) {
5795 if (*arg == '/') {
5796 /* Only the first '/' seen is our seperator */
5797 if (!repl) {
5798 *idx++ = '\0';
5799 repl = idx;
5800 } else
5801 *idx++ = *arg;
5802 } else if (*arg != '\\') {
5803 *idx++ = *arg;
5804 } else {
5805 if (inquotes)
5806 arg++;
5807 else {
5808 if (*(arg + 1) != '\\')
5809 goto single_backslash;
5810 arg += 2;
5811 }
5812
5813 switch (*arg) {
5814 case 'n': c = '\n'; break;
5815 case 'r': c = '\r'; break;
5816 case 't': c = '\t'; break;
5817 case 'v': c = '\v'; break;
5818 case 'f': c = '\f'; break;
5819 case 'b': c = '\b'; break;
5820 case 'a': c = '\a'; break;
5821 case '\\':
5822 if (*(arg + 1) != '\\' && !inquotes)
5823 goto single_backslash;
5824 arg++;
5825 /* FALLTHROUGH */
5826 case '\0':
5827 /* Trailing backslash, just stuff one in the buffer
5828 * and backup arg so the loop will exit.
5829 */
5830 c = '\\';
5831 if (!*arg)
5832 arg--;
5833 break;
5834 default:
5835 c = *arg;
5836 if (isdigit(c)) {
5837 /* It's an octal number, parse it. */
5838 int i;
5839 c = 0;
5840
5841 for (i = 0; *arg && i < 3; arg++, i++) {
5842 if (*arg >= '8' || *arg < '0')
5843 ash_msg_and_raise_error("Invalid octal char in pattern");
5844// TODO: number() instead? It does error checking...
5845 c = (c << 3) + atoi(arg);
5846 }
5847 /* back off one (so outer loop can do it) */
5848 arg--;
5849 }
5850 }
5851 *idx++ = c;
5852 }
5853 }
5854 *idx = *arg;
5855
5856 return repl;
5857
5858 single_backslash:
5859 ash_msg_and_raise_error("single backslash unexpected");
5860 /* NOTREACHED */
5861}
5862#endif /* ENABLE_ASH_BASH_COMPAT */
5863
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005864static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005865subevalvar(char *p, char *str, int strloc, int subtype,
5866 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005868 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005869 char *startp;
5870 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005871 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005872 USE_ASH_BASH_COMPAT(char *repl = NULL;)
5873 USE_ASH_BASH_COMPAT(char null = '\0';)
5874 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
5875 int saveherefd = herefd;
5876 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005877 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005878 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005879
5880 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005881 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5882 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005883 STPUTC('\0', expdest);
5884 herefd = saveherefd;
5885 argbackq = saveargbackq;
5886 startp = stackblock() + startloc;
5887
5888 switch (subtype) {
5889 case VSASSIGN:
5890 setvar(str, startp, 0);
5891 amount = startp - expdest;
5892 STADJUST(amount, expdest);
5893 return startp;
5894
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005895#if ENABLE_ASH_BASH_COMPAT
5896 case VSSUBSTR:
5897 loc = str = stackblock() + strloc;
5898// TODO: number() instead? It does error checking...
5899 pos = atoi(loc);
5900 len = str - startp - 1;
5901
5902 /* *loc != '\0', guaranteed by parser */
5903 if (quotes) {
5904 char *ptr;
5905
5906 /* We must adjust the length by the number of escapes we find. */
5907 for (ptr = startp; ptr < (str - 1); ptr++) {
5908 if(*ptr == CTLESC) {
5909 len--;
5910 ptr++;
5911 }
5912 }
5913 }
5914 orig_len = len;
5915
5916 if (*loc++ == ':') {
5917// TODO: number() instead? It does error checking...
5918 len = atoi(loc);
5919 } else {
5920 len = orig_len;
5921 while (*loc && *loc != ':')
5922 loc++;
5923 if (*loc++ == ':')
5924// TODO: number() instead? It does error checking...
5925 len = atoi(loc);
5926 }
5927 if (pos >= orig_len) {
5928 pos = 0;
5929 len = 0;
5930 }
5931 if (len > (orig_len - pos))
5932 len = orig_len - pos;
5933
5934 for (str = startp; pos; str++, pos--) {
5935 if (quotes && *str == CTLESC)
5936 str++;
5937 }
5938 for (loc = startp; len; len--) {
5939 if (quotes && *str == CTLESC)
5940 *loc++ = *str++;
5941 *loc++ = *str++;
5942 }
5943 *loc = '\0';
5944 amount = loc - expdest;
5945 STADJUST(amount, expdest);
5946 return loc;
5947#endif
5948
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005949 case VSQUESTION:
5950 varunset(p, str, startp, varflags);
5951 /* NOTREACHED */
5952 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005953 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005954
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005955 /* We'll comeback here if we grow the stack while handling
5956 * a VSREPLACE or VSREPLACEALL, since our pointers into the
5957 * stack will need rebasing, and we'll need to remove our work
5958 * areas each time
5959 */
5960 USE_ASH_BASH_COMPAT(restart:)
5961
5962 amount = expdest - ((char *)stackblock() + resetloc);
5963 STADJUST(-amount, expdest);
5964 startp = stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005965
5966 rmesc = startp;
5967 rmescend = stackblock() + strloc;
5968 if (quotes) {
5969 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5970 if (rmesc != startp) {
5971 rmescend = expdest;
5972 startp = stackblock() + startloc;
5973 }
5974 }
5975 rmescend--;
5976 str = stackblock() + strloc;
5977 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005978 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005979
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005980#if ENABLE_ASH_BASH_COMPAT
5981 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
5982 char *idx, *end, *restart_detect;
5983
5984 if(!repl) {
5985 repl = parse_sub_pattern(str, varflags & VSQUOTE);
5986 if (!repl)
5987 repl = &null;
5988 }
5989
5990 /* If there's no pattern to match, return the expansion unmolested */
5991 if (*str == '\0')
5992 return 0;
5993
5994 len = 0;
5995 idx = startp;
5996 end = str - 1;
5997 while (idx < end) {
5998 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
5999 if (!loc) {
6000 /* No match, advance */
6001 restart_detect = stackblock();
6002 STPUTC(*idx, expdest);
6003 if (quotes && *idx == CTLESC) {
6004 idx++;
6005 len++;
6006 STPUTC(*idx, expdest);
6007 }
6008 if (stackblock() != restart_detect)
6009 goto restart;
6010 idx++;
6011 len++;
6012 rmesc++;
6013 continue;
6014 }
6015
6016 if (subtype == VSREPLACEALL) {
6017 while (idx < loc) {
6018 if (quotes && *idx == CTLESC)
6019 idx++;
6020 idx++;
6021 rmesc++;
6022 }
6023 } else
6024 idx = loc;
6025
6026 for (loc = repl; *loc; loc++) {
6027 restart_detect = stackblock();
6028 STPUTC(*loc, expdest);
6029 if (stackblock() != restart_detect)
6030 goto restart;
6031 len++;
6032 }
6033
6034 if (subtype == VSREPLACE) {
6035 while (*idx) {
6036 restart_detect = stackblock();
6037 STPUTC(*idx, expdest);
6038 if (stackblock() != restart_detect)
6039 goto restart;
6040 len++;
6041 idx++;
6042 }
6043 break;
6044 }
6045 }
6046
6047 /* We've put the replaced text into a buffer at workloc, now
6048 * move it to the right place and adjust the stack.
6049 */
6050 startp = stackblock() + startloc;
6051 STPUTC('\0', expdest);
6052 memmove(startp, stackblock() + workloc, len);
6053 startp[len++] = '\0';
6054 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6055 STADJUST(-amount, expdest);
6056 return startp;
6057 }
6058#endif /* ENABLE_ASH_BASH_COMPAT */
6059
6060 subtype -= VSTRIMRIGHT;
6061#if DEBUG
6062 if (subtype < 0 || subtype > 7)
6063 abort();
6064#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6066 zero = subtype >> 1;
6067 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6068 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6069
6070 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6071 if (loc) {
6072 if (zero) {
6073 memmove(startp, loc, str - loc);
6074 loc = startp + (str - loc) - 1;
6075 }
6076 *loc = '\0';
6077 amount = loc - expdest;
6078 STADJUST(amount, expdest);
6079 }
6080 return loc;
6081}
6082
6083/*
6084 * Add the value of a specialized variable to the stack string.
6085 */
6086static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006087varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006088{
6089 int num;
6090 char *p;
6091 int i;
6092 int sep = 0;
6093 int sepq = 0;
6094 ssize_t len = 0;
6095 char **ap;
6096 int syntax;
6097 int quoted = varflags & VSQUOTE;
6098 int subtype = varflags & VSTYPE;
6099 int quotes = flags & (EXP_FULL | EXP_CASE);
6100
6101 if (quoted && (flags & EXP_FULL))
6102 sep = 1 << CHAR_BIT;
6103
6104 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6105 switch (*name) {
6106 case '$':
6107 num = rootpid;
6108 goto numvar;
6109 case '?':
6110 num = exitstatus;
6111 goto numvar;
6112 case '#':
6113 num = shellparam.nparam;
6114 goto numvar;
6115 case '!':
6116 num = backgndpid;
6117 if (num == 0)
6118 return -1;
6119 numvar:
6120 len = cvtnum(num);
6121 break;
6122 case '-':
6123 p = makestrspace(NOPTS, expdest);
6124 for (i = NOPTS - 1; i >= 0; i--) {
6125 if (optlist[i]) {
6126 USTPUTC(optletters(i), p);
6127 len++;
6128 }
6129 }
6130 expdest = p;
6131 break;
6132 case '@':
6133 if (sep)
6134 goto param;
6135 /* fall through */
6136 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006137 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006138 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6139 sepq = 1;
6140 param:
6141 ap = shellparam.p;
6142 if (!ap)
6143 return -1;
6144 while ((p = *ap++)) {
6145 size_t partlen;
6146
6147 partlen = strlen(p);
6148 len += partlen;
6149
6150 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6151 memtodest(p, partlen, syntax, quotes);
6152
6153 if (*ap && sep) {
6154 char *q;
6155
6156 len++;
6157 if (subtype == VSPLUS || subtype == VSLENGTH) {
6158 continue;
6159 }
6160 q = expdest;
6161 if (sepq)
6162 STPUTC(CTLESC, q);
6163 STPUTC(sep, q);
6164 expdest = q;
6165 }
6166 }
6167 return len;
6168 case '0':
6169 case '1':
6170 case '2':
6171 case '3':
6172 case '4':
6173 case '5':
6174 case '6':
6175 case '7':
6176 case '8':
6177 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006178// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006179 num = atoi(name);
6180 if (num < 0 || num > shellparam.nparam)
6181 return -1;
6182 p = num ? shellparam.p[num - 1] : arg0;
6183 goto value;
6184 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006185 /* NB: name has form "VAR=..." */
6186
6187 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6188 * which should be considered before we check variables. */
6189 if (var_str_list) {
6190 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6191 p = NULL;
6192 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006193 char *str, *eq;
6194 str = var_str_list->text;
6195 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006196 if (!eq) /* stop at first non-assignment */
6197 break;
6198 eq++;
6199 if (name_len == (eq - str)
6200 && strncmp(str, name, name_len) == 0) {
6201 p = eq;
6202 /* goto value; - WRONG! */
6203 /* think "A=1 A=2 B=$A" */
6204 }
6205 var_str_list = var_str_list->next;
6206 } while (var_str_list);
6207 if (p)
6208 goto value;
6209 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006210 p = lookupvar(name);
6211 value:
6212 if (!p)
6213 return -1;
6214
6215 len = strlen(p);
6216 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6217 memtodest(p, len, syntax, quotes);
6218 return len;
6219 }
6220
6221 if (subtype == VSPLUS || subtype == VSLENGTH)
6222 STADJUST(-len, expdest);
6223 return len;
6224}
6225
6226/*
6227 * Expand a variable, and return a pointer to the next character in the
6228 * input string.
6229 */
6230static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006231evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006232{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006233 char varflags;
6234 char subtype;
6235 char quoted;
6236 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006237 char *var;
6238 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006239 int startloc;
6240 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006241
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006242 varflags = *p++;
6243 subtype = varflags & VSTYPE;
6244 quoted = varflags & VSQUOTE;
6245 var = p;
6246 easy = (!quoted || (*var == '@' && shellparam.nparam));
6247 startloc = expdest - (char *)stackblock();
6248 p = strchr(p, '=') + 1;
6249
6250 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006251 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006252 if (varflags & VSNUL)
6253 varlen--;
6254
6255 if (subtype == VSPLUS) {
6256 varlen = -1 - varlen;
6257 goto vsplus;
6258 }
6259
6260 if (subtype == VSMINUS) {
6261 vsplus:
6262 if (varlen < 0) {
6263 argstr(
6264 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006265 (quoted ? EXP_QWORD : EXP_WORD),
6266 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006267 );
6268 goto end;
6269 }
6270 if (easy)
6271 goto record;
6272 goto end;
6273 }
6274
6275 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6276 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006277 if (subevalvar(p, var, /* strloc: */ 0,
6278 subtype, startloc, varflags,
6279 /* quotes: */ 0,
6280 var_str_list)
6281 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006282 varflags &= ~VSNUL;
6283 /*
6284 * Remove any recorded regions beyond
6285 * start of variable
6286 */
6287 removerecordregions(startloc);
6288 goto again;
6289 }
6290 goto end;
6291 }
6292 if (easy)
6293 goto record;
6294 goto end;
6295 }
6296
6297 if (varlen < 0 && uflag)
6298 varunset(p, var, 0, 0);
6299
6300 if (subtype == VSLENGTH) {
6301 cvtnum(varlen > 0 ? varlen : 0);
6302 goto record;
6303 }
6304
6305 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006306 if (easy)
6307 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006308 goto end;
6309 }
6310
6311#if DEBUG
6312 switch (subtype) {
6313 case VSTRIMLEFT:
6314 case VSTRIMLEFTMAX:
6315 case VSTRIMRIGHT:
6316 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006317#if ENABLE_ASH_BASH_COMPAT
6318 case VSSUBSTR:
6319 case VSREPLACE:
6320 case VSREPLACEALL:
6321#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322 break;
6323 default:
6324 abort();
6325 }
6326#endif
6327
6328 if (varlen >= 0) {
6329 /*
6330 * Terminate the string and start recording the pattern
6331 * right after it
6332 */
6333 STPUTC('\0', expdest);
6334 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006335 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6336 startloc, varflags,
6337 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6338 var_str_list)
6339 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006340 int amount = expdest - (
6341 (char *)stackblock() + patloc - 1
6342 );
6343 STADJUST(-amount, expdest);
6344 }
6345 /* Remove any recorded regions beyond start of variable */
6346 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006347 record:
6348 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349 }
6350
6351 end:
6352 if (subtype != VSNORMAL) { /* skip to end of alternative */
6353 int nesting = 1;
6354 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006355 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 if (c == CTLESC)
6357 p++;
6358 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6359 if (varlen >= 0)
6360 argbackq = argbackq->next;
6361 } else if (c == CTLVAR) {
6362 if ((*p++ & VSTYPE) != VSNORMAL)
6363 nesting++;
6364 } else if (c == CTLENDVAR) {
6365 if (--nesting == 0)
6366 break;
6367 }
6368 }
6369 }
6370 return p;
6371}
6372
6373/*
6374 * Break the argument string into pieces based upon IFS and add the
6375 * strings to the argument list. The regions of the string to be
6376 * searched for IFS characters have been stored by recordregion.
6377 */
6378static void
6379ifsbreakup(char *string, struct arglist *arglist)
6380{
6381 struct ifsregion *ifsp;
6382 struct strlist *sp;
6383 char *start;
6384 char *p;
6385 char *q;
6386 const char *ifs, *realifs;
6387 int ifsspc;
6388 int nulonly;
6389
6390 start = string;
6391 if (ifslastp != NULL) {
6392 ifsspc = 0;
6393 nulonly = 0;
6394 realifs = ifsset() ? ifsval() : defifs;
6395 ifsp = &ifsfirst;
6396 do {
6397 p = string + ifsp->begoff;
6398 nulonly = ifsp->nulonly;
6399 ifs = nulonly ? nullstr : realifs;
6400 ifsspc = 0;
6401 while (p < string + ifsp->endoff) {
6402 q = p;
6403 if (*p == CTLESC)
6404 p++;
6405 if (!strchr(ifs, *p)) {
6406 p++;
6407 continue;
6408 }
6409 if (!nulonly)
6410 ifsspc = (strchr(defifs, *p) != NULL);
6411 /* Ignore IFS whitespace at start */
6412 if (q == start && ifsspc) {
6413 p++;
6414 start = p;
6415 continue;
6416 }
6417 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006418 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006419 sp->text = start;
6420 *arglist->lastp = sp;
6421 arglist->lastp = &sp->next;
6422 p++;
6423 if (!nulonly) {
6424 for (;;) {
6425 if (p >= string + ifsp->endoff) {
6426 break;
6427 }
6428 q = p;
6429 if (*p == CTLESC)
6430 p++;
6431 if (strchr(ifs, *p) == NULL ) {
6432 p = q;
6433 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006434 }
6435 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436 if (ifsspc) {
6437 p++;
6438 ifsspc = 0;
6439 } else {
6440 p = q;
6441 break;
6442 }
6443 } else
6444 p++;
6445 }
6446 }
6447 start = p;
6448 } /* while */
6449 ifsp = ifsp->next;
6450 } while (ifsp != NULL);
6451 if (nulonly)
6452 goto add;
6453 }
6454
6455 if (!*start)
6456 return;
6457
6458 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006459 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006460 sp->text = start;
6461 *arglist->lastp = sp;
6462 arglist->lastp = &sp->next;
6463}
6464
6465static void
6466ifsfree(void)
6467{
6468 struct ifsregion *p;
6469
6470 INT_OFF;
6471 p = ifsfirst.next;
6472 do {
6473 struct ifsregion *ifsp;
6474 ifsp = p->next;
6475 free(p);
6476 p = ifsp;
6477 } while (p);
6478 ifslastp = NULL;
6479 ifsfirst.next = NULL;
6480 INT_ON;
6481}
6482
6483/*
6484 * Add a file name to the list.
6485 */
6486static void
6487addfname(const char *name)
6488{
6489 struct strlist *sp;
6490
Denis Vlasenko597906c2008-02-20 16:38:54 +00006491 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006492 sp->text = ststrdup(name);
6493 *exparg.lastp = sp;
6494 exparg.lastp = &sp->next;
6495}
6496
6497static char *expdir;
6498
6499/*
6500 * Do metacharacter (i.e. *, ?, [...]) expansion.
6501 */
6502static void
6503expmeta(char *enddir, char *name)
6504{
6505 char *p;
6506 const char *cp;
6507 char *start;
6508 char *endname;
6509 int metaflag;
6510 struct stat statb;
6511 DIR *dirp;
6512 struct dirent *dp;
6513 int atend;
6514 int matchdot;
6515
6516 metaflag = 0;
6517 start = name;
6518 for (p = name; *p; p++) {
6519 if (*p == '*' || *p == '?')
6520 metaflag = 1;
6521 else if (*p == '[') {
6522 char *q = p + 1;
6523 if (*q == '!')
6524 q++;
6525 for (;;) {
6526 if (*q == '\\')
6527 q++;
6528 if (*q == '/' || *q == '\0')
6529 break;
6530 if (*++q == ']') {
6531 metaflag = 1;
6532 break;
6533 }
6534 }
6535 } else if (*p == '\\')
6536 p++;
6537 else if (*p == '/') {
6538 if (metaflag)
6539 goto out;
6540 start = p + 1;
6541 }
6542 }
6543 out:
6544 if (metaflag == 0) { /* we've reached the end of the file name */
6545 if (enddir != expdir)
6546 metaflag++;
6547 p = name;
6548 do {
6549 if (*p == '\\')
6550 p++;
6551 *enddir++ = *p;
6552 } while (*p++);
6553 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6554 addfname(expdir);
6555 return;
6556 }
6557 endname = p;
6558 if (name < start) {
6559 p = name;
6560 do {
6561 if (*p == '\\')
6562 p++;
6563 *enddir++ = *p++;
6564 } while (p < start);
6565 }
6566 if (enddir == expdir) {
6567 cp = ".";
6568 } else if (enddir == expdir + 1 && *expdir == '/') {
6569 cp = "/";
6570 } else {
6571 cp = expdir;
6572 enddir[-1] = '\0';
6573 }
6574 dirp = opendir(cp);
6575 if (dirp == NULL)
6576 return;
6577 if (enddir != expdir)
6578 enddir[-1] = '/';
6579 if (*endname == 0) {
6580 atend = 1;
6581 } else {
6582 atend = 0;
6583 *endname++ = '\0';
6584 }
6585 matchdot = 0;
6586 p = start;
6587 if (*p == '\\')
6588 p++;
6589 if (*p == '.')
6590 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006591 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006592 if (dp->d_name[0] == '.' && ! matchdot)
6593 continue;
6594 if (pmatch(start, dp->d_name)) {
6595 if (atend) {
6596 strcpy(enddir, dp->d_name);
6597 addfname(expdir);
6598 } else {
6599 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6600 continue;
6601 p[-1] = '/';
6602 expmeta(p, endname);
6603 }
6604 }
6605 }
6606 closedir(dirp);
6607 if (! atend)
6608 endname[-1] = '/';
6609}
6610
6611static struct strlist *
6612msort(struct strlist *list, int len)
6613{
6614 struct strlist *p, *q = NULL;
6615 struct strlist **lpp;
6616 int half;
6617 int n;
6618
6619 if (len <= 1)
6620 return list;
6621 half = len >> 1;
6622 p = list;
6623 for (n = half; --n >= 0; ) {
6624 q = p;
6625 p = p->next;
6626 }
6627 q->next = NULL; /* terminate first half of list */
6628 q = msort(list, half); /* sort first half of list */
6629 p = msort(p, len - half); /* sort second half */
6630 lpp = &list;
6631 for (;;) {
6632#if ENABLE_LOCALE_SUPPORT
6633 if (strcoll(p->text, q->text) < 0)
6634#else
6635 if (strcmp(p->text, q->text) < 0)
6636#endif
6637 {
6638 *lpp = p;
6639 lpp = &p->next;
6640 p = *lpp;
6641 if (p == NULL) {
6642 *lpp = q;
6643 break;
6644 }
6645 } else {
6646 *lpp = q;
6647 lpp = &q->next;
6648 q = *lpp;
6649 if (q == NULL) {
6650 *lpp = p;
6651 break;
6652 }
6653 }
6654 }
6655 return list;
6656}
6657
6658/*
6659 * Sort the results of file name expansion. It calculates the number of
6660 * strings to sort and then calls msort (short for merge sort) to do the
6661 * work.
6662 */
6663static struct strlist *
6664expsort(struct strlist *str)
6665{
6666 int len;
6667 struct strlist *sp;
6668
6669 len = 0;
6670 for (sp = str; sp; sp = sp->next)
6671 len++;
6672 return msort(str, len);
6673}
6674
6675static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006676expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006677{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006678 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006679 '*', '?', '[', 0
6680 };
6681 /* TODO - EXP_REDIR */
6682
6683 while (str) {
6684 struct strlist **savelastp;
6685 struct strlist *sp;
6686 char *p;
6687
6688 if (fflag)
6689 goto nometa;
6690 if (!strpbrk(str->text, metachars))
6691 goto nometa;
6692 savelastp = exparg.lastp;
6693
6694 INT_OFF;
6695 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6696 {
6697 int i = strlen(str->text);
6698 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6699 }
6700
6701 expmeta(expdir, p);
6702 free(expdir);
6703 if (p != str->text)
6704 free(p);
6705 INT_ON;
6706 if (exparg.lastp == savelastp) {
6707 /*
6708 * no matches
6709 */
6710 nometa:
6711 *exparg.lastp = str;
6712 rmescapes(str->text);
6713 exparg.lastp = &str->next;
6714 } else {
6715 *exparg.lastp = NULL;
6716 *savelastp = sp = expsort(*savelastp);
6717 while (sp->next != NULL)
6718 sp = sp->next;
6719 exparg.lastp = &sp->next;
6720 }
6721 str = str->next;
6722 }
6723}
6724
6725/*
6726 * Perform variable substitution and command substitution on an argument,
6727 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6728 * perform splitting and file name expansion. When arglist is NULL, perform
6729 * here document expansion.
6730 */
6731static void
6732expandarg(union node *arg, struct arglist *arglist, int flag)
6733{
6734 struct strlist *sp;
6735 char *p;
6736
6737 argbackq = arg->narg.backquote;
6738 STARTSTACKSTR(expdest);
6739 ifsfirst.next = NULL;
6740 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006741 argstr(arg->narg.text, flag,
6742 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006743 p = _STPUTC('\0', expdest);
6744 expdest = p - 1;
6745 if (arglist == NULL) {
6746 return; /* here document expanded */
6747 }
6748 p = grabstackstr(p);
6749 exparg.lastp = &exparg.list;
6750 /*
6751 * TODO - EXP_REDIR
6752 */
6753 if (flag & EXP_FULL) {
6754 ifsbreakup(p, &exparg);
6755 *exparg.lastp = NULL;
6756 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006757 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006758 } else {
6759 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6760 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006761 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006762 sp->text = p;
6763 *exparg.lastp = sp;
6764 exparg.lastp = &sp->next;
6765 }
6766 if (ifsfirst.next)
6767 ifsfree();
6768 *exparg.lastp = NULL;
6769 if (exparg.list) {
6770 *arglist->lastp = exparg.list;
6771 arglist->lastp = exparg.lastp;
6772 }
6773}
6774
6775/*
6776 * Expand shell variables and backquotes inside a here document.
6777 */
6778static void
6779expandhere(union node *arg, int fd)
6780{
6781 herefd = fd;
6782 expandarg(arg, (struct arglist *)NULL, 0);
6783 full_write(fd, stackblock(), expdest - (char *)stackblock());
6784}
6785
6786/*
6787 * Returns true if the pattern matches the string.
6788 */
6789static int
6790patmatch(char *pattern, const char *string)
6791{
6792 return pmatch(preglob(pattern, 0, 0), string);
6793}
6794
6795/*
6796 * See if a pattern matches in a case statement.
6797 */
6798static int
6799casematch(union node *pattern, char *val)
6800{
6801 struct stackmark smark;
6802 int result;
6803
6804 setstackmark(&smark);
6805 argbackq = pattern->narg.backquote;
6806 STARTSTACKSTR(expdest);
6807 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006808 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6809 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006810 STACKSTRNUL(expdest);
6811 result = patmatch(stackblock(), val);
6812 popstackmark(&smark);
6813 return result;
6814}
6815
6816
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006817/* ============ find_command */
6818
6819struct builtincmd {
6820 const char *name;
6821 int (*builtin)(int, char **);
6822 /* unsigned flags; */
6823};
6824#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006825/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006826 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006827#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006828#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006829
6830struct cmdentry {
6831 int cmdtype;
6832 union param {
6833 int index;
6834 const struct builtincmd *cmd;
6835 struct funcnode *func;
6836 } u;
6837};
6838/* values of cmdtype */
6839#define CMDUNKNOWN -1 /* no entry in table for command */
6840#define CMDNORMAL 0 /* command is an executable program */
6841#define CMDFUNCTION 1 /* command is a shell function */
6842#define CMDBUILTIN 2 /* command is a shell builtin */
6843
6844/* action to find_command() */
6845#define DO_ERR 0x01 /* prints errors */
6846#define DO_ABS 0x02 /* checks absolute paths */
6847#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6848#define DO_ALTPATH 0x08 /* using alternate path */
6849#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6850
6851static void find_command(char *, struct cmdentry *, int, const char *);
6852
6853
6854/* ============ Hashing commands */
6855
6856/*
6857 * When commands are first encountered, they are entered in a hash table.
6858 * This ensures that a full path search will not have to be done for them
6859 * on each invocation.
6860 *
6861 * We should investigate converting to a linear search, even though that
6862 * would make the command name "hash" a misnomer.
6863 */
6864
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006865#define ARB 1 /* actual size determined at run time */
6866
6867struct tblentry {
6868 struct tblentry *next; /* next entry in hash chain */
6869 union param param; /* definition of builtin function */
6870 short cmdtype; /* index identifying command */
6871 char rehash; /* if set, cd done since entry created */
6872 char cmdname[ARB]; /* name of command */
6873};
6874
Denis Vlasenko01631112007-12-16 17:20:38 +00006875static struct tblentry **cmdtable;
6876#define INIT_G_cmdtable() do { \
6877 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6878} while (0)
6879
6880static int builtinloc = -1; /* index in path of %builtin, or -1 */
6881
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006882
6883static void
6884tryexec(char *cmd, char **argv, char **envp)
6885{
6886 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006887
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006888#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006889 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006890 int a = find_applet_by_name(cmd);
6891 if (a >= 0) {
6892 if (APPLET_IS_NOEXEC(a))
6893 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006894 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006895 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006896 /* If they called chroot or otherwise made the binary no longer
6897 * executable, fall through */
6898 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006899 }
6900#endif
6901
6902 repeat:
6903#ifdef SYSV
6904 do {
6905 execve(cmd, argv, envp);
6906 } while (errno == EINTR);
6907#else
6908 execve(cmd, argv, envp);
6909#endif
6910 if (repeated++) {
6911 free(argv);
6912 } else if (errno == ENOEXEC) {
6913 char **ap;
6914 char **new;
6915
6916 for (ap = argv; *ap; ap++)
6917 ;
6918 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6919 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006920 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006921 ap += 2;
6922 argv++;
6923 while ((*ap++ = *argv++))
Denis Vlasenko597906c2008-02-20 16:38:54 +00006924 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006925 argv = new;
6926 goto repeat;
6927 }
6928}
6929
6930/*
6931 * Exec a program. Never returns. If you change this routine, you may
6932 * have to change the find_command routine as well.
6933 */
6934#define environment() listvars(VEXPORT, VUNSET, 0)
6935static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6936static void
6937shellexec(char **argv, const char *path, int idx)
6938{
6939 char *cmdname;
6940 int e;
6941 char **envp;
6942 int exerrno;
6943
6944 clearredir(1);
6945 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006946 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006947#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006948 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006949#endif
6950 ) {
6951 tryexec(argv[0], argv, envp);
6952 e = errno;
6953 } else {
6954 e = ENOENT;
6955 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6956 if (--idx < 0 && pathopt == NULL) {
6957 tryexec(cmdname, argv, envp);
6958 if (errno != ENOENT && errno != ENOTDIR)
6959 e = errno;
6960 }
6961 stunalloc(cmdname);
6962 }
6963 }
6964
6965 /* Map to POSIX errors */
6966 switch (e) {
6967 case EACCES:
6968 exerrno = 126;
6969 break;
6970 case ENOENT:
6971 exerrno = 127;
6972 break;
6973 default:
6974 exerrno = 2;
6975 break;
6976 }
6977 exitstatus = exerrno;
6978 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6979 argv[0], e, suppressint ));
6980 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6981 /* NOTREACHED */
6982}
6983
6984static void
6985printentry(struct tblentry *cmdp)
6986{
6987 int idx;
6988 const char *path;
6989 char *name;
6990
6991 idx = cmdp->param.index;
6992 path = pathval();
6993 do {
6994 name = padvance(&path, cmdp->cmdname);
6995 stunalloc(name);
6996 } while (--idx >= 0);
6997 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6998}
6999
7000/*
7001 * Clear out command entries. The argument specifies the first entry in
7002 * PATH which has changed.
7003 */
7004static void
7005clearcmdentry(int firstchange)
7006{
7007 struct tblentry **tblp;
7008 struct tblentry **pp;
7009 struct tblentry *cmdp;
7010
7011 INT_OFF;
7012 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7013 pp = tblp;
7014 while ((cmdp = *pp) != NULL) {
7015 if ((cmdp->cmdtype == CMDNORMAL &&
7016 cmdp->param.index >= firstchange)
7017 || (cmdp->cmdtype == CMDBUILTIN &&
7018 builtinloc >= firstchange)
7019 ) {
7020 *pp = cmdp->next;
7021 free(cmdp);
7022 } else {
7023 pp = &cmdp->next;
7024 }
7025 }
7026 }
7027 INT_ON;
7028}
7029
7030/*
7031 * Locate a command in the command hash table. If "add" is nonzero,
7032 * add the command to the table if it is not already present. The
7033 * variable "lastcmdentry" is set to point to the address of the link
7034 * pointing to the entry, so that delete_cmd_entry can delete the
7035 * entry.
7036 *
7037 * Interrupts must be off if called with add != 0.
7038 */
7039static struct tblentry **lastcmdentry;
7040
7041static struct tblentry *
7042cmdlookup(const char *name, int add)
7043{
7044 unsigned int hashval;
7045 const char *p;
7046 struct tblentry *cmdp;
7047 struct tblentry **pp;
7048
7049 p = name;
7050 hashval = (unsigned char)*p << 4;
7051 while (*p)
7052 hashval += (unsigned char)*p++;
7053 hashval &= 0x7FFF;
7054 pp = &cmdtable[hashval % CMDTABLESIZE];
7055 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7056 if (strcmp(cmdp->cmdname, name) == 0)
7057 break;
7058 pp = &cmdp->next;
7059 }
7060 if (add && cmdp == NULL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00007061 cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007062 + strlen(name) + 1);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007063 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007064 cmdp->cmdtype = CMDUNKNOWN;
7065 strcpy(cmdp->cmdname, name);
7066 }
7067 lastcmdentry = pp;
7068 return cmdp;
7069}
7070
7071/*
7072 * Delete the command entry returned on the last lookup.
7073 */
7074static void
7075delete_cmd_entry(void)
7076{
7077 struct tblentry *cmdp;
7078
7079 INT_OFF;
7080 cmdp = *lastcmdentry;
7081 *lastcmdentry = cmdp->next;
7082 if (cmdp->cmdtype == CMDFUNCTION)
7083 freefunc(cmdp->param.func);
7084 free(cmdp);
7085 INT_ON;
7086}
7087
7088/*
7089 * Add a new command entry, replacing any existing command entry for
7090 * the same name - except special builtins.
7091 */
7092static void
7093addcmdentry(char *name, struct cmdentry *entry)
7094{
7095 struct tblentry *cmdp;
7096
7097 cmdp = cmdlookup(name, 1);
7098 if (cmdp->cmdtype == CMDFUNCTION) {
7099 freefunc(cmdp->param.func);
7100 }
7101 cmdp->cmdtype = entry->cmdtype;
7102 cmdp->param = entry->u;
7103 cmdp->rehash = 0;
7104}
7105
7106static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007107hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007108{
7109 struct tblentry **pp;
7110 struct tblentry *cmdp;
7111 int c;
7112 struct cmdentry entry;
7113 char *name;
7114
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007115 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007116 clearcmdentry(0);
7117 return 0;
7118 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007119
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007120 if (*argptr == NULL) {
7121 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7122 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7123 if (cmdp->cmdtype == CMDNORMAL)
7124 printentry(cmdp);
7125 }
7126 }
7127 return 0;
7128 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007129
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007130 c = 0;
7131 while ((name = *argptr) != NULL) {
7132 cmdp = cmdlookup(name, 0);
7133 if (cmdp != NULL
7134 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007135 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7136 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007137 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007138 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007139 find_command(name, &entry, DO_ERR, pathval());
7140 if (entry.cmdtype == CMDUNKNOWN)
7141 c = 1;
7142 argptr++;
7143 }
7144 return c;
7145}
7146
7147/*
7148 * Called when a cd is done. Marks all commands so the next time they
7149 * are executed they will be rehashed.
7150 */
7151static void
7152hashcd(void)
7153{
7154 struct tblentry **pp;
7155 struct tblentry *cmdp;
7156
7157 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7158 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007159 if (cmdp->cmdtype == CMDNORMAL
7160 || (cmdp->cmdtype == CMDBUILTIN
7161 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7162 && builtinloc > 0)
7163 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007164 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007165 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007166 }
7167 }
7168}
7169
7170/*
7171 * Fix command hash table when PATH changed.
7172 * Called before PATH is changed. The argument is the new value of PATH;
7173 * pathval() still returns the old value at this point.
7174 * Called with interrupts off.
7175 */
7176static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007177changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007178{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007179 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007180 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007181 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007182 int idx_bltin;
7183
7184 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007185 firstchange = 9999; /* assume no change */
7186 idx = 0;
7187 idx_bltin = -1;
7188 for (;;) {
7189 if (*old != *new) {
7190 firstchange = idx;
7191 if ((*old == '\0' && *new == ':')
7192 || (*old == ':' && *new == '\0'))
7193 firstchange++;
7194 old = new; /* ignore subsequent differences */
7195 }
7196 if (*new == '\0')
7197 break;
7198 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7199 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007200 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007201 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007202 new++, old++;
7203 }
7204 if (builtinloc < 0 && idx_bltin >= 0)
7205 builtinloc = idx_bltin; /* zap builtins */
7206 if (builtinloc >= 0 && idx_bltin < 0)
7207 firstchange = 0;
7208 clearcmdentry(firstchange);
7209 builtinloc = idx_bltin;
7210}
7211
7212#define TEOF 0
7213#define TNL 1
7214#define TREDIR 2
7215#define TWORD 3
7216#define TSEMI 4
7217#define TBACKGND 5
7218#define TAND 6
7219#define TOR 7
7220#define TPIPE 8
7221#define TLP 9
7222#define TRP 10
7223#define TENDCASE 11
7224#define TENDBQUOTE 12
7225#define TNOT 13
7226#define TCASE 14
7227#define TDO 15
7228#define TDONE 16
7229#define TELIF 17
7230#define TELSE 18
7231#define TESAC 19
7232#define TFI 20
7233#define TFOR 21
7234#define TIF 22
7235#define TIN 23
7236#define TTHEN 24
7237#define TUNTIL 25
7238#define TWHILE 26
7239#define TBEGIN 27
7240#define TEND 28
7241
7242/* first char is indicating which tokens mark the end of a list */
7243static const char *const tokname_array[] = {
7244 "\1end of file",
7245 "\0newline",
7246 "\0redirection",
7247 "\0word",
7248 "\0;",
7249 "\0&",
7250 "\0&&",
7251 "\0||",
7252 "\0|",
7253 "\0(",
7254 "\1)",
7255 "\1;;",
7256 "\1`",
7257#define KWDOFFSET 13
7258 /* the following are keywords */
7259 "\0!",
7260 "\0case",
7261 "\1do",
7262 "\1done",
7263 "\1elif",
7264 "\1else",
7265 "\1esac",
7266 "\1fi",
7267 "\0for",
7268 "\0if",
7269 "\0in",
7270 "\1then",
7271 "\0until",
7272 "\0while",
7273 "\0{",
7274 "\1}",
7275};
7276
7277static const char *
7278tokname(int tok)
7279{
7280 static char buf[16];
7281
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007282//try this:
7283//if (tok < TSEMI) return tokname_array[tok] + 1;
7284//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7285//return buf;
7286
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007287 if (tok >= TSEMI)
7288 buf[0] = '"';
7289 sprintf(buf + (tok >= TSEMI), "%s%c",
7290 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7291 return buf;
7292}
7293
7294/* Wrapper around strcmp for qsort/bsearch/... */
7295static int
7296pstrcmp(const void *a, const void *b)
7297{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007298 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007299}
7300
7301static const char *const *
7302findkwd(const char *s)
7303{
7304 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007305 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7306 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007307}
7308
7309/*
7310 * Locate and print what a word is...
7311 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007312static int
7313describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007314{
7315 struct cmdentry entry;
7316 struct tblentry *cmdp;
7317#if ENABLE_ASH_ALIAS
7318 const struct alias *ap;
7319#endif
7320 const char *path = pathval();
7321
7322 if (describe_command_verbose) {
7323 out1str(command);
7324 }
7325
7326 /* First look at the keywords */
7327 if (findkwd(command)) {
7328 out1str(describe_command_verbose ? " is a shell keyword" : command);
7329 goto out;
7330 }
7331
7332#if ENABLE_ASH_ALIAS
7333 /* Then look at the aliases */
7334 ap = lookupalias(command, 0);
7335 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007336 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007337 out1str("alias ");
7338 printalias(ap);
7339 return 0;
7340 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007341 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342 goto out;
7343 }
7344#endif
7345 /* Then check if it is a tracked alias */
7346 cmdp = cmdlookup(command, 0);
7347 if (cmdp != NULL) {
7348 entry.cmdtype = cmdp->cmdtype;
7349 entry.u = cmdp->param;
7350 } else {
7351 /* Finally use brute force */
7352 find_command(command, &entry, DO_ABS, path);
7353 }
7354
7355 switch (entry.cmdtype) {
7356 case CMDNORMAL: {
7357 int j = entry.u.index;
7358 char *p;
7359 if (j == -1) {
7360 p = command;
7361 } else {
7362 do {
7363 p = padvance(&path, command);
7364 stunalloc(p);
7365 } while (--j >= 0);
7366 }
7367 if (describe_command_verbose) {
7368 out1fmt(" is%s %s",
7369 (cmdp ? " a tracked alias for" : nullstr), p
7370 );
7371 } else {
7372 out1str(p);
7373 }
7374 break;
7375 }
7376
7377 case CMDFUNCTION:
7378 if (describe_command_verbose) {
7379 out1str(" is a shell function");
7380 } else {
7381 out1str(command);
7382 }
7383 break;
7384
7385 case CMDBUILTIN:
7386 if (describe_command_verbose) {
7387 out1fmt(" is a %sshell builtin",
7388 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7389 "special " : nullstr
7390 );
7391 } else {
7392 out1str(command);
7393 }
7394 break;
7395
7396 default:
7397 if (describe_command_verbose) {
7398 out1str(": not found\n");
7399 }
7400 return 127;
7401 }
7402 out:
7403 outstr("\n", stdout);
7404 return 0;
7405}
7406
7407static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007408typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007410 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007412 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007413
Denis Vlasenko46846e22007-05-20 13:08:31 +00007414 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007415 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007416 i++;
7417 verbose = 0;
7418 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007419 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007420 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007421 }
7422 return err;
7423}
7424
7425#if ENABLE_ASH_CMDCMD
7426static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00007427commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007428{
7429 int c;
7430 enum {
7431 VERIFY_BRIEF = 1,
7432 VERIFY_VERBOSE = 2,
7433 } verify = 0;
7434
7435 while ((c = nextopt("pvV")) != '\0')
7436 if (c == 'V')
7437 verify |= VERIFY_VERBOSE;
7438 else if (c == 'v')
7439 verify |= VERIFY_BRIEF;
7440#if DEBUG
7441 else if (c != 'p')
7442 abort();
7443#endif
7444 if (verify)
7445 return describe_command(*argptr, verify - VERIFY_BRIEF);
7446
7447 return 0;
7448}
7449#endif
7450
7451
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007452/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007453
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454static int funcblocksize; /* size of structures in function */
7455static int funcstringsize; /* size of strings in node */
7456static void *funcblock; /* block to allocate function from */
7457static char *funcstring; /* block to allocate strings from */
7458
Eric Andersencb57d552001-06-28 07:25:16 +00007459/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007460#define EV_EXIT 01 /* exit after evaluating tree */
7461#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7462#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007463
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464static const short nodesize[26] = {
7465 SHELL_ALIGN(sizeof(struct ncmd)),
7466 SHELL_ALIGN(sizeof(struct npipe)),
7467 SHELL_ALIGN(sizeof(struct nredir)),
7468 SHELL_ALIGN(sizeof(struct nredir)),
7469 SHELL_ALIGN(sizeof(struct nredir)),
7470 SHELL_ALIGN(sizeof(struct nbinary)),
7471 SHELL_ALIGN(sizeof(struct nbinary)),
7472 SHELL_ALIGN(sizeof(struct nbinary)),
7473 SHELL_ALIGN(sizeof(struct nif)),
7474 SHELL_ALIGN(sizeof(struct nbinary)),
7475 SHELL_ALIGN(sizeof(struct nbinary)),
7476 SHELL_ALIGN(sizeof(struct nfor)),
7477 SHELL_ALIGN(sizeof(struct ncase)),
7478 SHELL_ALIGN(sizeof(struct nclist)),
7479 SHELL_ALIGN(sizeof(struct narg)),
7480 SHELL_ALIGN(sizeof(struct narg)),
7481 SHELL_ALIGN(sizeof(struct nfile)),
7482 SHELL_ALIGN(sizeof(struct nfile)),
7483 SHELL_ALIGN(sizeof(struct nfile)),
7484 SHELL_ALIGN(sizeof(struct nfile)),
7485 SHELL_ALIGN(sizeof(struct nfile)),
7486 SHELL_ALIGN(sizeof(struct ndup)),
7487 SHELL_ALIGN(sizeof(struct ndup)),
7488 SHELL_ALIGN(sizeof(struct nhere)),
7489 SHELL_ALIGN(sizeof(struct nhere)),
7490 SHELL_ALIGN(sizeof(struct nnot)),
7491};
7492
7493static void calcsize(union node *n);
7494
7495static void
7496sizenodelist(struct nodelist *lp)
7497{
7498 while (lp) {
7499 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7500 calcsize(lp->n);
7501 lp = lp->next;
7502 }
7503}
7504
7505static void
7506calcsize(union node *n)
7507{
7508 if (n == NULL)
7509 return;
7510 funcblocksize += nodesize[n->type];
7511 switch (n->type) {
7512 case NCMD:
7513 calcsize(n->ncmd.redirect);
7514 calcsize(n->ncmd.args);
7515 calcsize(n->ncmd.assign);
7516 break;
7517 case NPIPE:
7518 sizenodelist(n->npipe.cmdlist);
7519 break;
7520 case NREDIR:
7521 case NBACKGND:
7522 case NSUBSHELL:
7523 calcsize(n->nredir.redirect);
7524 calcsize(n->nredir.n);
7525 break;
7526 case NAND:
7527 case NOR:
7528 case NSEMI:
7529 case NWHILE:
7530 case NUNTIL:
7531 calcsize(n->nbinary.ch2);
7532 calcsize(n->nbinary.ch1);
7533 break;
7534 case NIF:
7535 calcsize(n->nif.elsepart);
7536 calcsize(n->nif.ifpart);
7537 calcsize(n->nif.test);
7538 break;
7539 case NFOR:
7540 funcstringsize += strlen(n->nfor.var) + 1;
7541 calcsize(n->nfor.body);
7542 calcsize(n->nfor.args);
7543 break;
7544 case NCASE:
7545 calcsize(n->ncase.cases);
7546 calcsize(n->ncase.expr);
7547 break;
7548 case NCLIST:
7549 calcsize(n->nclist.body);
7550 calcsize(n->nclist.pattern);
7551 calcsize(n->nclist.next);
7552 break;
7553 case NDEFUN:
7554 case NARG:
7555 sizenodelist(n->narg.backquote);
7556 funcstringsize += strlen(n->narg.text) + 1;
7557 calcsize(n->narg.next);
7558 break;
7559 case NTO:
7560 case NCLOBBER:
7561 case NFROM:
7562 case NFROMTO:
7563 case NAPPEND:
7564 calcsize(n->nfile.fname);
7565 calcsize(n->nfile.next);
7566 break;
7567 case NTOFD:
7568 case NFROMFD:
7569 calcsize(n->ndup.vname);
7570 calcsize(n->ndup.next);
7571 break;
7572 case NHERE:
7573 case NXHERE:
7574 calcsize(n->nhere.doc);
7575 calcsize(n->nhere.next);
7576 break;
7577 case NNOT:
7578 calcsize(n->nnot.com);
7579 break;
7580 };
7581}
7582
7583static char *
7584nodeckstrdup(char *s)
7585{
7586 char *rtn = funcstring;
7587
7588 strcpy(funcstring, s);
7589 funcstring += strlen(s) + 1;
7590 return rtn;
7591}
7592
7593static union node *copynode(union node *);
7594
7595static struct nodelist *
7596copynodelist(struct nodelist *lp)
7597{
7598 struct nodelist *start;
7599 struct nodelist **lpp;
7600
7601 lpp = &start;
7602 while (lp) {
7603 *lpp = funcblock;
7604 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7605 (*lpp)->n = copynode(lp->n);
7606 lp = lp->next;
7607 lpp = &(*lpp)->next;
7608 }
7609 *lpp = NULL;
7610 return start;
7611}
7612
7613static union node *
7614copynode(union node *n)
7615{
7616 union node *new;
7617
7618 if (n == NULL)
7619 return NULL;
7620 new = funcblock;
7621 funcblock = (char *) funcblock + nodesize[n->type];
7622
7623 switch (n->type) {
7624 case NCMD:
7625 new->ncmd.redirect = copynode(n->ncmd.redirect);
7626 new->ncmd.args = copynode(n->ncmd.args);
7627 new->ncmd.assign = copynode(n->ncmd.assign);
7628 break;
7629 case NPIPE:
7630 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7631 new->npipe.backgnd = n->npipe.backgnd;
7632 break;
7633 case NREDIR:
7634 case NBACKGND:
7635 case NSUBSHELL:
7636 new->nredir.redirect = copynode(n->nredir.redirect);
7637 new->nredir.n = copynode(n->nredir.n);
7638 break;
7639 case NAND:
7640 case NOR:
7641 case NSEMI:
7642 case NWHILE:
7643 case NUNTIL:
7644 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7645 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7646 break;
7647 case NIF:
7648 new->nif.elsepart = copynode(n->nif.elsepart);
7649 new->nif.ifpart = copynode(n->nif.ifpart);
7650 new->nif.test = copynode(n->nif.test);
7651 break;
7652 case NFOR:
7653 new->nfor.var = nodeckstrdup(n->nfor.var);
7654 new->nfor.body = copynode(n->nfor.body);
7655 new->nfor.args = copynode(n->nfor.args);
7656 break;
7657 case NCASE:
7658 new->ncase.cases = copynode(n->ncase.cases);
7659 new->ncase.expr = copynode(n->ncase.expr);
7660 break;
7661 case NCLIST:
7662 new->nclist.body = copynode(n->nclist.body);
7663 new->nclist.pattern = copynode(n->nclist.pattern);
7664 new->nclist.next = copynode(n->nclist.next);
7665 break;
7666 case NDEFUN:
7667 case NARG:
7668 new->narg.backquote = copynodelist(n->narg.backquote);
7669 new->narg.text = nodeckstrdup(n->narg.text);
7670 new->narg.next = copynode(n->narg.next);
7671 break;
7672 case NTO:
7673 case NCLOBBER:
7674 case NFROM:
7675 case NFROMTO:
7676 case NAPPEND:
7677 new->nfile.fname = copynode(n->nfile.fname);
7678 new->nfile.fd = n->nfile.fd;
7679 new->nfile.next = copynode(n->nfile.next);
7680 break;
7681 case NTOFD:
7682 case NFROMFD:
7683 new->ndup.vname = copynode(n->ndup.vname);
7684 new->ndup.dupfd = n->ndup.dupfd;
7685 new->ndup.fd = n->ndup.fd;
7686 new->ndup.next = copynode(n->ndup.next);
7687 break;
7688 case NHERE:
7689 case NXHERE:
7690 new->nhere.doc = copynode(n->nhere.doc);
7691 new->nhere.fd = n->nhere.fd;
7692 new->nhere.next = copynode(n->nhere.next);
7693 break;
7694 case NNOT:
7695 new->nnot.com = copynode(n->nnot.com);
7696 break;
7697 };
7698 new->type = n->type;
7699 return new;
7700}
7701
7702/*
7703 * Make a copy of a parse tree.
7704 */
7705static struct funcnode *
7706copyfunc(union node *n)
7707{
7708 struct funcnode *f;
7709 size_t blocksize;
7710
7711 funcblocksize = offsetof(struct funcnode, n);
7712 funcstringsize = 0;
7713 calcsize(n);
7714 blocksize = funcblocksize;
7715 f = ckmalloc(blocksize + funcstringsize);
7716 funcblock = (char *) f + offsetof(struct funcnode, n);
7717 funcstring = (char *) f + blocksize;
7718 copynode(n);
7719 f->count = 0;
7720 return f;
7721}
7722
7723/*
7724 * Define a shell function.
7725 */
7726static void
7727defun(char *name, union node *func)
7728{
7729 struct cmdentry entry;
7730
7731 INT_OFF;
7732 entry.cmdtype = CMDFUNCTION;
7733 entry.u.func = copyfunc(func);
7734 addcmdentry(name, &entry);
7735 INT_ON;
7736}
7737
7738static int evalskip; /* set if we are skipping commands */
7739/* reasons for skipping commands (see comment on breakcmd routine) */
7740#define SKIPBREAK (1 << 0)
7741#define SKIPCONT (1 << 1)
7742#define SKIPFUNC (1 << 2)
7743#define SKIPFILE (1 << 3)
7744#define SKIPEVAL (1 << 4)
7745static int skipcount; /* number of levels to skip */
7746static int funcnest; /* depth of function calls */
7747
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007748/* forward decl way out to parsing code - dotrap needs it */
7749static int evalstring(char *s, int mask);
7750
7751/*
7752 * Called to execute a trap. Perhaps we should avoid entering new trap
7753 * handlers while we are executing a trap handler.
7754 */
7755static int
7756dotrap(void)
7757{
7758 char *p;
7759 char *q;
7760 int i;
7761 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007762 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007763
7764 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007765 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007766 xbarrier();
7767
7768 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7769 if (!*q)
7770 continue;
7771 *q = '\0';
7772
7773 p = trap[i + 1];
7774 if (!p)
7775 continue;
7776 skip = evalstring(p, SKIPEVAL);
7777 exitstatus = savestatus;
7778 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007779 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007780 }
7781
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007782 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007783}
7784
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007785/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007786static void evalloop(union node *, int);
7787static void evalfor(union node *, int);
7788static void evalcase(union node *, int);
7789static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007790static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007791static void evalpipe(union node *, int);
7792static void evalcommand(union node *, int);
7793static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007794static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007795
Eric Andersen62483552001-07-10 06:09:16 +00007796/*
Eric Andersenc470f442003-07-28 09:56:35 +00007797 * Evaluate a parse tree. The value is left in the global variable
7798 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007799 */
Eric Andersenc470f442003-07-28 09:56:35 +00007800static void
7801evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007802{
Eric Andersenc470f442003-07-28 09:56:35 +00007803 int checkexit = 0;
7804 void (*evalfn)(union node *, int);
7805 unsigned isor;
7806 int status;
7807 if (n == NULL) {
7808 TRACE(("evaltree(NULL) called\n"));
7809 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007810 }
Eric Andersenc470f442003-07-28 09:56:35 +00007811 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007812 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007813 switch (n->type) {
7814 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007815#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007816 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007817 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007818 break;
7819#endif
7820 case NNOT:
7821 evaltree(n->nnot.com, EV_TESTED);
7822 status = !exitstatus;
7823 goto setstatus;
7824 case NREDIR:
7825 expredir(n->nredir.redirect);
7826 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7827 if (!status) {
7828 evaltree(n->nredir.n, flags & EV_TESTED);
7829 status = exitstatus;
7830 }
7831 popredir(0);
7832 goto setstatus;
7833 case NCMD:
7834 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007835 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007836 if (eflag && !(flags & EV_TESTED))
7837 checkexit = ~0;
7838 goto calleval;
7839 case NFOR:
7840 evalfn = evalfor;
7841 goto calleval;
7842 case NWHILE:
7843 case NUNTIL:
7844 evalfn = evalloop;
7845 goto calleval;
7846 case NSUBSHELL:
7847 case NBACKGND:
7848 evalfn = evalsubshell;
7849 goto calleval;
7850 case NPIPE:
7851 evalfn = evalpipe;
7852 goto checkexit;
7853 case NCASE:
7854 evalfn = evalcase;
7855 goto calleval;
7856 case NAND:
7857 case NOR:
7858 case NSEMI:
7859#if NAND + 1 != NOR
7860#error NAND + 1 != NOR
7861#endif
7862#if NOR + 1 != NSEMI
7863#error NOR + 1 != NSEMI
7864#endif
7865 isor = n->type - NAND;
7866 evaltree(
7867 n->nbinary.ch1,
7868 (flags | ((isor >> 1) - 1)) & EV_TESTED
7869 );
7870 if (!exitstatus == isor)
7871 break;
7872 if (!evalskip) {
7873 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007874 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007875 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007876 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007877 evalfn(n, flags);
7878 break;
7879 }
7880 break;
7881 case NIF:
7882 evaltree(n->nif.test, EV_TESTED);
7883 if (evalskip)
7884 break;
7885 if (exitstatus == 0) {
7886 n = n->nif.ifpart;
7887 goto evaln;
7888 } else if (n->nif.elsepart) {
7889 n = n->nif.elsepart;
7890 goto evaln;
7891 }
7892 goto success;
7893 case NDEFUN:
7894 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007895 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007896 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007897 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007898 exitstatus = status;
7899 break;
7900 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007901 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007902 if ((checkexit & exitstatus))
7903 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007904 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007905 goto exexit;
7906
7907 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007908 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007909 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007910 }
Eric Andersen62483552001-07-10 06:09:16 +00007911}
7912
Eric Andersenc470f442003-07-28 09:56:35 +00007913#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7914static
7915#endif
7916void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7917
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007918static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007919
7920static void
7921evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007922{
7923 int status;
7924
7925 loopnest++;
7926 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007927 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007928 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007929 int i;
7930
Eric Andersencb57d552001-06-28 07:25:16 +00007931 evaltree(n->nbinary.ch1, EV_TESTED);
7932 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007933 skipping:
7934 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007935 evalskip = 0;
7936 continue;
7937 }
7938 if (evalskip == SKIPBREAK && --skipcount <= 0)
7939 evalskip = 0;
7940 break;
7941 }
Eric Andersenc470f442003-07-28 09:56:35 +00007942 i = exitstatus;
7943 if (n->type != NWHILE)
7944 i = !i;
7945 if (i != 0)
7946 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007947 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007948 status = exitstatus;
7949 if (evalskip)
7950 goto skipping;
7951 }
7952 loopnest--;
7953 exitstatus = status;
7954}
7955
Eric Andersenc470f442003-07-28 09:56:35 +00007956static void
7957evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007958{
7959 struct arglist arglist;
7960 union node *argp;
7961 struct strlist *sp;
7962 struct stackmark smark;
7963
7964 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007965 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007966 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007967 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007968 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007969 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007970 if (evalskip)
7971 goto out;
7972 }
7973 *arglist.lastp = NULL;
7974
7975 exitstatus = 0;
7976 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007977 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007978 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007979 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007980 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007981 if (evalskip) {
7982 if (evalskip == SKIPCONT && --skipcount <= 0) {
7983 evalskip = 0;
7984 continue;
7985 }
7986 if (evalskip == SKIPBREAK && --skipcount <= 0)
7987 evalskip = 0;
7988 break;
7989 }
7990 }
7991 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007992 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007993 popstackmark(&smark);
7994}
7995
Eric Andersenc470f442003-07-28 09:56:35 +00007996static void
7997evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007998{
7999 union node *cp;
8000 union node *patp;
8001 struct arglist arglist;
8002 struct stackmark smark;
8003
8004 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008005 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008006 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008007 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008008 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008009 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8010 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008011 if (casematch(patp, arglist.list->text)) {
8012 if (evalskip == 0) {
8013 evaltree(cp->nclist.body, flags);
8014 }
8015 goto out;
8016 }
8017 }
8018 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008019 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008020 popstackmark(&smark);
8021}
8022
Eric Andersenc470f442003-07-28 09:56:35 +00008023/*
8024 * Kick off a subshell to evaluate a tree.
8025 */
Eric Andersenc470f442003-07-28 09:56:35 +00008026static void
8027evalsubshell(union node *n, int flags)
8028{
8029 struct job *jp;
8030 int backgnd = (n->type == NBACKGND);
8031 int status;
8032
8033 expredir(n->nredir.redirect);
8034 if (!backgnd && flags & EV_EXIT && !trap[0])
8035 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008036 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008037 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008038 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008039 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008040 flags |= EV_EXIT;
8041 if (backgnd)
8042 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008043 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008044 redirect(n->nredir.redirect, 0);
8045 evaltreenr(n->nredir.n, flags);
8046 /* never returns */
8047 }
8048 status = 0;
8049 if (! backgnd)
8050 status = waitforjob(jp);
8051 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008052 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008053}
8054
Eric Andersenc470f442003-07-28 09:56:35 +00008055/*
8056 * Compute the names of the files in a redirection list.
8057 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008058static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008059static void
8060expredir(union node *n)
8061{
8062 union node *redir;
8063
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008064 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008065 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008066
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008067 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008068 fn.lastp = &fn.list;
8069 switch (redir->type) {
8070 case NFROMTO:
8071 case NFROM:
8072 case NTO:
8073 case NCLOBBER:
8074 case NAPPEND:
8075 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
8076 redir->nfile.expfname = fn.list->text;
8077 break;
8078 case NFROMFD:
8079 case NTOFD:
8080 if (redir->ndup.vname) {
8081 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008082 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008083 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008084 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008085 }
8086 break;
8087 }
8088 }
8089}
8090
Eric Andersencb57d552001-06-28 07:25:16 +00008091/*
Eric Andersencb57d552001-06-28 07:25:16 +00008092 * Evaluate a pipeline. All the processes in the pipeline are children
8093 * of the process creating the pipeline. (This differs from some versions
8094 * of the shell, which make the last process in a pipeline the parent
8095 * of all the rest.)
8096 */
Eric Andersenc470f442003-07-28 09:56:35 +00008097static void
8098evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008099{
8100 struct job *jp;
8101 struct nodelist *lp;
8102 int pipelen;
8103 int prevfd;
8104 int pip[2];
8105
Eric Andersenc470f442003-07-28 09:56:35 +00008106 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008107 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008108 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008109 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008110 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008111 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008112 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008113 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008114 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008115 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008116 pip[1] = -1;
8117 if (lp->next) {
8118 if (pipe(pip) < 0) {
8119 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008120 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008121 }
8122 }
8123 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008124 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008125 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008126 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008127 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008128 if (prevfd > 0) {
8129 dup2(prevfd, 0);
8130 close(prevfd);
8131 }
8132 if (pip[1] > 1) {
8133 dup2(pip[1], 1);
8134 close(pip[1]);
8135 }
Eric Andersenc470f442003-07-28 09:56:35 +00008136 evaltreenr(lp->n, flags);
8137 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008138 }
8139 if (prevfd >= 0)
8140 close(prevfd);
8141 prevfd = pip[0];
8142 close(pip[1]);
8143 }
Eric Andersencb57d552001-06-28 07:25:16 +00008144 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008145 exitstatus = waitforjob(jp);
8146 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008147 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008148 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008149}
8150
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008151/*
8152 * Controls whether the shell is interactive or not.
8153 */
8154static void
8155setinteractive(int on)
8156{
8157 static int is_interactive;
8158
8159 if (++on == is_interactive)
8160 return;
8161 is_interactive = on;
8162 setsignal(SIGINT);
8163 setsignal(SIGQUIT);
8164 setsignal(SIGTERM);
8165#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8166 if (is_interactive > 1) {
8167 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008168 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008169
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008170 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008171 out1fmt(
8172 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008173 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008174 "Enter 'help' for a list of built-in commands."
8175 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008176 bb_banner);
8177 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008178 }
8179 }
8180#endif
8181}
8182
8183#if ENABLE_FEATURE_EDITING_VI
8184#define setvimode(on) do { \
8185 if (on) line_input_state->flags |= VI_MODE; \
8186 else line_input_state->flags &= ~VI_MODE; \
8187} while (0)
8188#else
8189#define setvimode(on) viflag = 0 /* forcibly keep the option off */
8190#endif
8191
8192static void
8193optschanged(void)
8194{
8195#if DEBUG
8196 opentrace();
8197#endif
8198 setinteractive(iflag);
8199 setjobctl(mflag);
8200 setvimode(viflag);
8201}
8202
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008203static struct localvar *localvars;
8204
8205/*
8206 * Called after a function returns.
8207 * Interrupts must be off.
8208 */
8209static void
8210poplocalvars(void)
8211{
8212 struct localvar *lvp;
8213 struct var *vp;
8214
8215 while ((lvp = localvars) != NULL) {
8216 localvars = lvp->next;
8217 vp = lvp->vp;
8218 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8219 if (vp == NULL) { /* $- saved */
8220 memcpy(optlist, lvp->text, sizeof(optlist));
8221 free((char*)lvp->text);
8222 optschanged();
8223 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8224 unsetvar(vp->text);
8225 } else {
8226 if (vp->func)
8227 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8228 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8229 free((char*)vp->text);
8230 vp->flags = lvp->flags;
8231 vp->text = lvp->text;
8232 }
8233 free(lvp);
8234 }
8235}
8236
8237static int
8238evalfun(struct funcnode *func, int argc, char **argv, int flags)
8239{
8240 volatile struct shparam saveparam;
8241 struct localvar *volatile savelocalvars;
8242 struct jmploc *volatile savehandler;
8243 struct jmploc jmploc;
8244 int e;
8245
8246 saveparam = shellparam;
8247 savelocalvars = localvars;
8248 e = setjmp(jmploc.loc);
8249 if (e) {
8250 goto funcdone;
8251 }
8252 INT_OFF;
8253 savehandler = exception_handler;
8254 exception_handler = &jmploc;
8255 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008256 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008257 func->count++;
8258 funcnest++;
8259 INT_ON;
8260 shellparam.nparam = argc - 1;
8261 shellparam.p = argv + 1;
8262#if ENABLE_ASH_GETOPTS
8263 shellparam.optind = 1;
8264 shellparam.optoff = -1;
8265#endif
8266 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008267 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008268 INT_OFF;
8269 funcnest--;
8270 freefunc(func);
8271 poplocalvars();
8272 localvars = savelocalvars;
8273 freeparam(&shellparam);
8274 shellparam = saveparam;
8275 exception_handler = savehandler;
8276 INT_ON;
8277 evalskip &= ~SKIPFUNC;
8278 return e;
8279}
8280
Denis Vlasenko131ae172007-02-18 13:00:19 +00008281#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008282static char **
8283parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008284{
8285 char *cp, c;
8286
8287 for (;;) {
8288 cp = *++argv;
8289 if (!cp)
8290 return 0;
8291 if (*cp++ != '-')
8292 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008293 c = *cp++;
8294 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008295 break;
8296 if (c == '-' && !*cp) {
8297 argv++;
8298 break;
8299 }
8300 do {
8301 switch (c) {
8302 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008303 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008304 break;
8305 default:
8306 /* run 'typecmd' for other options */
8307 return 0;
8308 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008309 c = *cp++;
8310 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008311 }
8312 return argv;
8313}
8314#endif
8315
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008316/*
8317 * Make a variable a local variable. When a variable is made local, it's
8318 * value and flags are saved in a localvar structure. The saved values
8319 * will be restored when the shell function returns. We handle the name
8320 * "-" as a special case.
8321 */
8322static void
8323mklocal(char *name)
8324{
8325 struct localvar *lvp;
8326 struct var **vpp;
8327 struct var *vp;
8328
8329 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008330 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008331 if (LONE_DASH(name)) {
8332 char *p;
8333 p = ckmalloc(sizeof(optlist));
8334 lvp->text = memcpy(p, optlist, sizeof(optlist));
8335 vp = NULL;
8336 } else {
8337 char *eq;
8338
8339 vpp = hashvar(name);
8340 vp = *findvar(vpp, name);
8341 eq = strchr(name, '=');
8342 if (vp == NULL) {
8343 if (eq)
8344 setvareq(name, VSTRFIXED);
8345 else
8346 setvar(name, NULL, VSTRFIXED);
8347 vp = *vpp; /* the new variable */
8348 lvp->flags = VUNSET;
8349 } else {
8350 lvp->text = vp->text;
8351 lvp->flags = vp->flags;
8352 vp->flags |= VSTRFIXED|VTEXTFIXED;
8353 if (eq)
8354 setvareq(name, 0);
8355 }
8356 }
8357 lvp->vp = vp;
8358 lvp->next = localvars;
8359 localvars = lvp;
8360 INT_ON;
8361}
8362
8363/*
8364 * The "local" command.
8365 */
8366static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008367localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008368{
8369 char *name;
8370
8371 argv = argptr;
8372 while ((name = *argv++) != NULL) {
8373 mklocal(name);
8374 }
8375 return 0;
8376}
8377
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008378static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008379falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008380{
8381 return 1;
8382}
8383
8384static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008385truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008386{
8387 return 0;
8388}
8389
8390static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008391execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008392{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008393 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008394 iflag = 0; /* exit on error */
8395 mflag = 0;
8396 optschanged();
8397 shellexec(argv + 1, pathval(), 0);
8398 }
8399 return 0;
8400}
8401
8402/*
8403 * The return command.
8404 */
8405static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008406returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008407{
8408 /*
8409 * If called outside a function, do what ksh does;
8410 * skip the rest of the file.
8411 */
8412 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8413 return argv[1] ? number(argv[1]) : exitstatus;
8414}
8415
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008416/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008417static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008418static int dotcmd(int, char **);
8419static int evalcmd(int, char **);
8420#if ENABLE_ASH_BUILTIN_ECHO
8421static int echocmd(int, char **);
8422#endif
8423#if ENABLE_ASH_BUILTIN_TEST
8424static int testcmd(int, char **);
8425#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008426static int exitcmd(int, char **);
8427static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008428#if ENABLE_ASH_GETOPTS
8429static int getoptscmd(int, char **);
8430#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008431#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008432static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008433#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008434#if ENABLE_ASH_MATH_SUPPORT
8435static int letcmd(int, char **);
8436#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008437static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008438static int setcmd(int, char **);
8439static int shiftcmd(int, char **);
8440static int timescmd(int, char **);
8441static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008442static int umaskcmd(int, char **);
8443static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008444static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008445
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008446#define BUILTIN_NOSPEC "0"
8447#define BUILTIN_SPECIAL "1"
8448#define BUILTIN_REGULAR "2"
8449#define BUILTIN_SPEC_REG "3"
8450#define BUILTIN_ASSIGN "4"
8451#define BUILTIN_SPEC_ASSG "5"
8452#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008453#define BUILTIN_SPEC_REG_ASSG "7"
8454
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008455/* We do not handle [[ expr ]] bashism bash-compatibly,
8456 * we make it a synonym of [ expr ].
8457 * Basically, word splitting and pathname expansion should NOT be performed
8458 * Examples:
8459 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8460 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8461 * Additional operators:
8462 * || and && should work as -o and -a
8463 * =~ regexp match
8464 * Apart from the above, [[ expr ]] should work as [ expr ]
8465 */
8466
8467/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008468static const struct builtincmd builtintab[] = {
8469 { BUILTIN_SPEC_REG ".", dotcmd },
8470 { BUILTIN_SPEC_REG ":", truecmd },
8471#if ENABLE_ASH_BUILTIN_TEST
8472 { BUILTIN_REGULAR "[", testcmd },
8473 { BUILTIN_REGULAR "[[", testcmd },
8474#endif
8475#if ENABLE_ASH_ALIAS
8476 { BUILTIN_REG_ASSG "alias", aliascmd },
8477#endif
8478#if JOBS
8479 { BUILTIN_REGULAR "bg", fg_bgcmd },
8480#endif
8481 { BUILTIN_SPEC_REG "break", breakcmd },
8482 { BUILTIN_REGULAR "cd", cdcmd },
8483 { BUILTIN_NOSPEC "chdir", cdcmd },
8484#if ENABLE_ASH_CMDCMD
8485 { BUILTIN_REGULAR "command", commandcmd },
8486#endif
8487 { BUILTIN_SPEC_REG "continue", breakcmd },
8488#if ENABLE_ASH_BUILTIN_ECHO
8489 { BUILTIN_REGULAR "echo", echocmd },
8490#endif
8491 { BUILTIN_SPEC_REG "eval", evalcmd },
8492 { BUILTIN_SPEC_REG "exec", execcmd },
8493 { BUILTIN_SPEC_REG "exit", exitcmd },
8494 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8495 { BUILTIN_REGULAR "false", falsecmd },
8496#if JOBS
8497 { BUILTIN_REGULAR "fg", fg_bgcmd },
8498#endif
8499#if ENABLE_ASH_GETOPTS
8500 { BUILTIN_REGULAR "getopts", getoptscmd },
8501#endif
8502 { BUILTIN_NOSPEC "hash", hashcmd },
8503#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8504 { BUILTIN_NOSPEC "help", helpcmd },
8505#endif
8506#if JOBS
8507 { BUILTIN_REGULAR "jobs", jobscmd },
8508 { BUILTIN_REGULAR "kill", killcmd },
8509#endif
8510#if ENABLE_ASH_MATH_SUPPORT
8511 { BUILTIN_NOSPEC "let", letcmd },
8512#endif
8513 { BUILTIN_ASSIGN "local", localcmd },
8514 { BUILTIN_NOSPEC "pwd", pwdcmd },
8515 { BUILTIN_REGULAR "read", readcmd },
8516 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8517 { BUILTIN_SPEC_REG "return", returncmd },
8518 { BUILTIN_SPEC_REG "set", setcmd },
8519 { BUILTIN_SPEC_REG "shift", shiftcmd },
8520 { BUILTIN_SPEC_REG "source", dotcmd },
8521#if ENABLE_ASH_BUILTIN_TEST
8522 { BUILTIN_REGULAR "test", testcmd },
8523#endif
8524 { BUILTIN_SPEC_REG "times", timescmd },
8525 { BUILTIN_SPEC_REG "trap", trapcmd },
8526 { BUILTIN_REGULAR "true", truecmd },
8527 { BUILTIN_NOSPEC "type", typecmd },
8528 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8529 { BUILTIN_REGULAR "umask", umaskcmd },
8530#if ENABLE_ASH_ALIAS
8531 { BUILTIN_REGULAR "unalias", unaliascmd },
8532#endif
8533 { BUILTIN_SPEC_REG "unset", unsetcmd },
8534 { BUILTIN_REGULAR "wait", waitcmd },
8535};
8536
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008537
8538#define COMMANDCMD (builtintab + 5 + \
8539 2 * ENABLE_ASH_BUILTIN_TEST + \
8540 ENABLE_ASH_ALIAS + \
8541 ENABLE_ASH_JOB_CONTROL)
8542#define EXECCMD (builtintab + 7 + \
8543 2 * ENABLE_ASH_BUILTIN_TEST + \
8544 ENABLE_ASH_ALIAS + \
8545 ENABLE_ASH_JOB_CONTROL + \
8546 ENABLE_ASH_CMDCMD + \
8547 ENABLE_ASH_BUILTIN_ECHO)
8548
8549/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008550 * Search the table of builtin commands.
8551 */
8552static struct builtincmd *
8553find_builtin(const char *name)
8554{
8555 struct builtincmd *bp;
8556
8557 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008558 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008559 pstrcmp
8560 );
8561 return bp;
8562}
8563
8564/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008565 * Execute a simple command.
8566 */
8567static int back_exitstatus; /* exit status of backquoted command */
8568static int
8569isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008570{
8571 const char *q = endofname(p);
8572 if (p == q)
8573 return 0;
8574 return *q == '=';
8575}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008576static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008577bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008578{
8579 /* Preserve exitstatus of a previous possible redirection
8580 * as POSIX mandates */
8581 return back_exitstatus;
8582}
Eric Andersenc470f442003-07-28 09:56:35 +00008583static void
8584evalcommand(union node *cmd, int flags)
8585{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008586 static const struct builtincmd null_bltin = {
8587 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008588 };
Eric Andersenc470f442003-07-28 09:56:35 +00008589 struct stackmark smark;
8590 union node *argp;
8591 struct arglist arglist;
8592 struct arglist varlist;
8593 char **argv;
8594 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008595 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008596 struct cmdentry cmdentry;
8597 struct job *jp;
8598 char *lastarg;
8599 const char *path;
8600 int spclbltin;
8601 int cmd_is_exec;
8602 int status;
8603 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008604 struct builtincmd *bcmd;
8605 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008606
8607 /* First expand the arguments. */
8608 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8609 setstackmark(&smark);
8610 back_exitstatus = 0;
8611
8612 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008613 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008614 varlist.lastp = &varlist.list;
8615 *varlist.lastp = NULL;
8616 arglist.lastp = &arglist.list;
8617 *arglist.lastp = NULL;
8618
8619 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008620 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008621 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8622 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8623 }
8624
Eric Andersenc470f442003-07-28 09:56:35 +00008625 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8626 struct strlist **spp;
8627
8628 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008629 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008630 expandarg(argp, &arglist, EXP_VARTILDE);
8631 else
8632 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8633
Eric Andersenc470f442003-07-28 09:56:35 +00008634 for (sp = *spp; sp; sp = sp->next)
8635 argc++;
8636 }
8637
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008638 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008639 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008640 TRACE(("evalcommand arg: %s\n", sp->text));
8641 *nargv++ = sp->text;
8642 }
8643 *nargv = NULL;
8644
8645 lastarg = NULL;
8646 if (iflag && funcnest == 0 && argc > 0)
8647 lastarg = nargv[-1];
8648
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008649 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008650 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008651 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008652
8653 path = vpath.text;
8654 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8655 struct strlist **spp;
8656 char *p;
8657
8658 spp = varlist.lastp;
8659 expandarg(argp, &varlist, EXP_VARTILDE);
8660
8661 /*
8662 * Modify the command lookup path, if a PATH= assignment
8663 * is present
8664 */
8665 p = (*spp)->text;
8666 if (varequal(p, path))
8667 path = p;
8668 }
8669
8670 /* Print the command if xflag is set. */
8671 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008672 int n;
8673 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008674
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008675 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008676 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008677
8678 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008679 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008680 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008681 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008682 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008683 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008684 p--;
8685 }
8686 }
8687 sp = arglist.list;
8688 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008689 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008690 }
8691
8692 cmd_is_exec = 0;
8693 spclbltin = -1;
8694
8695 /* Now locate the command. */
8696 if (argc) {
8697 const char *oldpath;
8698 int cmd_flag = DO_ERR;
8699
8700 path += 5;
8701 oldpath = path;
8702 for (;;) {
8703 find_command(argv[0], &cmdentry, cmd_flag, path);
8704 if (cmdentry.cmdtype == CMDUNKNOWN) {
8705 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008706 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008707 goto bail;
8708 }
8709
8710 /* implement bltin and command here */
8711 if (cmdentry.cmdtype != CMDBUILTIN)
8712 break;
8713 if (spclbltin < 0)
8714 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8715 if (cmdentry.u.cmd == EXECCMD)
8716 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008717#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008718 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008719 path = oldpath;
8720 nargv = parse_command_args(argv, &path);
8721 if (!nargv)
8722 break;
8723 argc -= nargv - argv;
8724 argv = nargv;
8725 cmd_flag |= DO_NOFUNC;
8726 } else
8727#endif
8728 break;
8729 }
8730 }
8731
8732 if (status) {
8733 /* We have a redirection error. */
8734 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008735 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008736 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008737 exitstatus = status;
8738 goto out;
8739 }
8740
8741 /* Execute the command. */
8742 switch (cmdentry.cmdtype) {
8743 default:
8744 /* Fork off a child process if necessary. */
8745 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008746 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008747 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008748 if (forkshell(jp, cmd, FORK_FG) != 0) {
8749 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008750 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008751 break;
8752 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008753 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008754 }
8755 listsetvar(varlist.list, VEXPORT|VSTACK);
8756 shellexec(argv, path, cmdentry.u.index);
8757 /* NOTREACHED */
8758
8759 case CMDBUILTIN:
8760 cmdenviron = varlist.list;
8761 if (cmdenviron) {
8762 struct strlist *list = cmdenviron;
8763 int i = VNOSET;
8764 if (spclbltin > 0 || argc == 0) {
8765 i = 0;
8766 if (cmd_is_exec && argc > 1)
8767 i = VEXPORT;
8768 }
8769 listsetvar(list, i);
8770 }
8771 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8772 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008773 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008774 if (i == EXEXIT)
8775 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008776 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008777 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008778 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008779 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008780 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008781 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008782 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008783 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008784 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008785 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008786 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008787 }
8788 break;
8789
8790 case CMDFUNCTION:
8791 listsetvar(varlist.list, 0);
8792 if (evalfun(cmdentry.u.func, argc, argv, flags))
8793 goto raise;
8794 break;
8795 }
8796
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008797 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008798 popredir(cmd_is_exec);
8799 if (lastarg)
8800 /* dsl: I think this is intended to be used to support
8801 * '_' in 'vi' command mode during line editing...
8802 * However I implemented that within libedit itself.
8803 */
8804 setvar("_", lastarg, 0);
8805 popstackmark(&smark);
8806}
8807
8808static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008809evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8810{
Eric Andersenc470f442003-07-28 09:56:35 +00008811 char *volatile savecmdname;
8812 struct jmploc *volatile savehandler;
8813 struct jmploc jmploc;
8814 int i;
8815
8816 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008817 i = setjmp(jmploc.loc);
8818 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008819 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008820 savehandler = exception_handler;
8821 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008822 commandname = argv[0];
8823 argptr = argv + 1;
8824 optptr = NULL; /* initialize nextopt */
8825 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008826 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008827 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008828 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008829 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008830 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008831// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008832 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008833
8834 return i;
8835}
8836
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008837static int
8838goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008839{
8840 return !*endofname(p);
8841}
8842
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008843
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008844/*
8845 * Search for a command. This is called before we fork so that the
8846 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008847 * the child. The check for "goodname" is an overly conservative
8848 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008849 */
Eric Andersenc470f442003-07-28 09:56:35 +00008850static void
8851prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008852{
8853 struct cmdentry entry;
8854
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008855 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8856 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008857}
8858
Eric Andersencb57d552001-06-28 07:25:16 +00008859
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008860/* ============ Builtin commands
8861 *
8862 * Builtin commands whose functions are closely tied to evaluation
8863 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008864 */
8865
8866/*
Eric Andersencb57d552001-06-28 07:25:16 +00008867 * Handle break and continue commands. Break, continue, and return are
8868 * all handled by setting the evalskip flag. The evaluation routines
8869 * above all check this flag, and if it is set they start skipping
8870 * commands rather than executing them. The variable skipcount is
8871 * the number of loops to break/continue, or the number of function
8872 * levels to return. (The latter is always 1.) It should probably
8873 * be an error to break out of more loops than exist, but it isn't
8874 * in the standard shell so we don't make it one here.
8875 */
Eric Andersenc470f442003-07-28 09:56:35 +00008876static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00008877breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008878{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008879 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00008880
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008881 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008882 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008883 if (n > loopnest)
8884 n = loopnest;
8885 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008886 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008887 skipcount = n;
8888 }
8889 return 0;
8890}
8891
Eric Andersenc470f442003-07-28 09:56:35 +00008892
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008893/* ============ input.c
8894 *
Eric Andersen90898442003-08-06 11:20:52 +00008895 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008896 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008897
Eric Andersenc470f442003-07-28 09:56:35 +00008898#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008899
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008900enum {
8901 INPUT_PUSH_FILE = 1,
8902 INPUT_NOFILE_OK = 2,
8903};
Eric Andersencb57d552001-06-28 07:25:16 +00008904
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008905static int plinno = 1; /* input line number */
8906/* number of characters left in input buffer */
8907static int parsenleft; /* copy of parsefile->nleft */
8908static int parselleft; /* copy of parsefile->lleft */
8909/* next character in input buffer */
8910static char *parsenextc; /* copy of parsefile->nextc */
8911
8912static int checkkwd;
8913/* values of checkkwd variable */
8914#define CHKALIAS 0x1
8915#define CHKKWD 0x2
8916#define CHKNL 0x4
8917
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008918static void
8919popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008920{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008921 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008922
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008923 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008924#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008925 if (sp->ap) {
8926 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8927 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008928 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008929 if (sp->string != sp->ap->val) {
8930 free(sp->string);
8931 }
8932 sp->ap->flag &= ~ALIASINUSE;
8933 if (sp->ap->flag & ALIASDEAD) {
8934 unalias(sp->ap->name);
8935 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008936 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008937#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008938 parsenextc = sp->prevstring;
8939 parsenleft = sp->prevnleft;
8940/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8941 parsefile->strpush = sp->prev;
8942 if (sp != &(parsefile->basestrpush))
8943 free(sp);
8944 INT_ON;
8945}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008946
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008947static int
8948preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008949{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008950 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008951 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008952 parsenextc = buf;
8953
Denis Vlasenko38f63192007-01-22 09:03:07 +00008954#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00008955 retry:
Eric Andersenc470f442003-07-28 09:56:35 +00008956 if (!iflag || parsefile->fd)
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008957 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008958 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008959#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008960 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008961#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008962 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8963 if (nr == 0) {
8964 /* Ctrl+C pressed */
8965 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008966 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008967 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008968 raise(SIGINT);
8969 return 1;
8970 }
Eric Andersenc470f442003-07-28 09:56:35 +00008971 goto retry;
8972 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008973 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008974 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008975 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008976 }
Eric Andersencb57d552001-06-28 07:25:16 +00008977 }
8978#else
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008979 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008980#endif
8981
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008982#if 0
8983/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008984 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008985 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008986 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008987 if (flags >= 0 && (flags & O_NONBLOCK)) {
8988 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008989 if (fcntl(0, F_SETFL, flags) >= 0) {
8990 out2str("sh: turning off NDELAY mode\n");
8991 goto retry;
8992 }
8993 }
8994 }
8995 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008996#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008997 return nr;
8998}
8999
9000/*
9001 * Refill the input buffer and return the next input character:
9002 *
9003 * 1) If a string was pushed back on the input, pop it;
9004 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
9005 * from a string so we can't refill the buffer, return EOF.
9006 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9007 * 4) Process input up to the next newline, deleting nul characters.
9008 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009009static int
Eric Andersenc470f442003-07-28 09:56:35 +00009010preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009011{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009012 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009013 int more;
9014 char savec;
9015
9016 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009017#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00009018 if (parsenleft == -1 && parsefile->strpush->ap &&
9019 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00009020 return PEOA;
9021 }
Eric Andersen2870d962001-07-02 17:27:21 +00009022#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009023 popstring();
9024 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009025 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009026 }
9027 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
9028 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009029 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00009030
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009031 more = parselleft;
9032 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009033 again:
9034 more = preadfd();
9035 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009036 parselleft = parsenleft = EOF_NLEFT;
9037 return PEOF;
9038 }
9039 }
9040
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009041 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00009042
9043 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009044 for (;;) {
9045 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00009046
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009047 more--;
9048 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009049
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009050 if (!c)
9051 memmove(q, q + 1, more);
9052 else {
9053 q++;
9054 if (c == '\n') {
9055 parsenleft = q - parsenextc - 1;
9056 break;
9057 }
Eric Andersencb57d552001-06-28 07:25:16 +00009058 }
9059
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009060 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009061 parsenleft = q - parsenextc - 1;
9062 if (parsenleft < 0)
9063 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009064 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009065 }
9066 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009067 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009068
9069 savec = *q;
9070 *q = '\0';
9071
9072 if (vflag) {
9073 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00009074 }
9075
9076 *q = savec;
9077
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009078 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009079}
9080
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009081#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009082static int
9083pgetc(void)
9084{
9085 return pgetc_as_macro();
9086}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009087
9088#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
9089#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009090#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009091#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009092#endif
9093
9094/*
9095 * Same as pgetc(), but ignores PEOA.
9096 */
9097#if ENABLE_ASH_ALIAS
9098static int
9099pgetc2(void)
9100{
9101 int c;
9102
9103 do {
9104 c = pgetc_macro();
9105 } while (c == PEOA);
9106 return c;
9107}
9108#else
9109static int
9110pgetc2(void)
9111{
9112 return pgetc_macro();
9113}
9114#endif
9115
9116/*
9117 * Read a line from the script.
9118 */
9119static char *
9120pfgets(char *line, int len)
9121{
9122 char *p = line;
9123 int nleft = len;
9124 int c;
9125
9126 while (--nleft > 0) {
9127 c = pgetc2();
9128 if (c == PEOF) {
9129 if (p == line)
9130 return NULL;
9131 break;
9132 }
9133 *p++ = c;
9134 if (c == '\n')
9135 break;
9136 }
9137 *p = '\0';
9138 return line;
9139}
9140
Eric Andersenc470f442003-07-28 09:56:35 +00009141/*
9142 * Undo the last call to pgetc. Only one character may be pushed back.
9143 * PEOF may be pushed back.
9144 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009145static void
Eric Andersenc470f442003-07-28 09:56:35 +00009146pungetc(void)
9147{
9148 parsenleft++;
9149 parsenextc--;
9150}
Eric Andersencb57d552001-06-28 07:25:16 +00009151
9152/*
9153 * Push a string back onto the input at this current parsefile level.
9154 * We handle aliases this way.
9155 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00009156#if !ENABLE_ASH_ALIAS
9157#define pushstring(s, ap) pushstring(s)
9158#endif
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009159static void
Denis Vlasenko85c24712008-03-17 09:04:04 +00009160pushstring(char *s, struct alias *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00009161{
Eric Andersencb57d552001-06-28 07:25:16 +00009162 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009163 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00009164
Eric Andersenc470f442003-07-28 09:56:35 +00009165 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009166 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009167/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
9168 if (parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00009169 sp = ckzalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00009170 sp->prev = parsefile->strpush;
9171 parsefile->strpush = sp;
9172 } else
9173 sp = parsefile->strpush = &(parsefile->basestrpush);
9174 sp->prevstring = parsenextc;
9175 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009176#if ENABLE_ASH_ALIAS
Denis Vlasenko85c24712008-03-17 09:04:04 +00009177 sp->ap = ap;
Eric Andersencb57d552001-06-28 07:25:16 +00009178 if (ap) {
Denis Vlasenko85c24712008-03-17 09:04:04 +00009179 ap->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00009180 sp->string = s;
9181 }
Eric Andersen2870d962001-07-02 17:27:21 +00009182#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009183 parsenextc = s;
9184 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009185 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009186}
9187
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009188/*
9189 * To handle the "." command, a stack of input files is used. Pushfile
9190 * adds a new entry to the stack and popfile restores the previous level.
9191 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009192static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009193pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009194{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009195 struct parsefile *pf;
9196
9197 parsefile->nleft = parsenleft;
9198 parsefile->lleft = parselleft;
9199 parsefile->nextc = parsenextc;
9200 parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009201 pf = ckzalloc(sizeof(*pf));
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009202 pf->prev = parsefile;
9203 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009204 /*pf->strpush = NULL; - ckzalloc did it */
9205 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009206 parsefile = pf;
9207}
9208
9209static void
9210popfile(void)
9211{
9212 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009213
Denis Vlasenkob012b102007-02-19 22:43:01 +00009214 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009215 if (pf->fd >= 0)
9216 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009217 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218 while (pf->strpush)
9219 popstring();
9220 parsefile = pf->prev;
9221 free(pf);
9222 parsenleft = parsefile->nleft;
9223 parselleft = parsefile->lleft;
9224 parsenextc = parsefile->nextc;
9225 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009226 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009227}
9228
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009229/*
9230 * Return to top level.
9231 */
9232static void
9233popallfiles(void)
9234{
9235 while (parsefile != &basepf)
9236 popfile();
9237}
9238
9239/*
9240 * Close the file(s) that the shell is reading commands from. Called
9241 * after a fork is done.
9242 */
9243static void
9244closescript(void)
9245{
9246 popallfiles();
9247 if (parsefile->fd > 0) {
9248 close(parsefile->fd);
9249 parsefile->fd = 0;
9250 }
9251}
9252
9253/*
9254 * Like setinputfile, but takes an open file descriptor. Call this with
9255 * interrupts off.
9256 */
9257static void
9258setinputfd(int fd, int push)
9259{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009260 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009261 if (push) {
9262 pushfile();
9263 parsefile->buf = 0;
9264 }
9265 parsefile->fd = fd;
9266 if (parsefile->buf == NULL)
9267 parsefile->buf = ckmalloc(IBUFSIZ);
9268 parselleft = parsenleft = 0;
9269 plinno = 1;
9270}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009271
Eric Andersenc470f442003-07-28 09:56:35 +00009272/*
9273 * Set the input to take input from a file. If push is set, push the
9274 * old input onto the stack first.
9275 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009276static int
9277setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009278{
9279 int fd;
9280 int fd2;
9281
Denis Vlasenkob012b102007-02-19 22:43:01 +00009282 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009283 fd = open(fname, O_RDONLY);
9284 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009285 if (flags & INPUT_NOFILE_OK)
9286 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009287 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009288 }
Eric Andersenc470f442003-07-28 09:56:35 +00009289 if (fd < 10) {
9290 fd2 = copyfd(fd, 10);
9291 close(fd);
9292 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009293 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009294 fd = fd2;
9295 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009296 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009297 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009298 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009299 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009300}
9301
Eric Andersencb57d552001-06-28 07:25:16 +00009302/*
9303 * Like setinputfile, but takes input from a string.
9304 */
Eric Andersenc470f442003-07-28 09:56:35 +00009305static void
9306setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009307{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009308 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009309 pushfile();
9310 parsenextc = string;
9311 parsenleft = strlen(string);
9312 parsefile->buf = NULL;
9313 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009314 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009315}
9316
9317
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009318/* ============ mail.c
9319 *
9320 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009321 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009322
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009323#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009324
Eric Andersencb57d552001-06-28 07:25:16 +00009325#define MAXMBOXES 10
9326
Eric Andersenc470f442003-07-28 09:56:35 +00009327/* times of mailboxes */
9328static time_t mailtime[MAXMBOXES];
9329/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009330static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009331
Eric Andersencb57d552001-06-28 07:25:16 +00009332/*
Eric Andersenc470f442003-07-28 09:56:35 +00009333 * Print appropriate message(s) if mail has arrived.
9334 * If mail_var_path_changed is set,
9335 * then the value of MAIL has mail_var_path_changed,
9336 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009337 */
Eric Andersenc470f442003-07-28 09:56:35 +00009338static void
9339chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009340{
Eric Andersencb57d552001-06-28 07:25:16 +00009341 const char *mpath;
9342 char *p;
9343 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009344 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009345 struct stackmark smark;
9346 struct stat statb;
9347
Eric Andersencb57d552001-06-28 07:25:16 +00009348 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009349 mpath = mpathset() ? mpathval() : mailval();
9350 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009351 p = padvance(&mpath, nullstr);
9352 if (p == NULL)
9353 break;
9354 if (*p == '\0')
9355 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009356 for (q = p; *q; q++)
9357 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009358#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009359 if (q[-1] != '/')
9360 abort();
9361#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009362 q[-1] = '\0'; /* delete trailing '/' */
9363 if (stat(p, &statb) < 0) {
9364 *mtp = 0;
9365 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009366 }
Eric Andersenc470f442003-07-28 09:56:35 +00009367 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9368 fprintf(
9369 stderr, snlfmt,
9370 pathopt ? pathopt : "you have mail"
9371 );
9372 }
9373 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009374 }
Eric Andersenc470f442003-07-28 09:56:35 +00009375 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009376 popstackmark(&smark);
9377}
Eric Andersencb57d552001-06-28 07:25:16 +00009378
Eric Andersenc470f442003-07-28 09:56:35 +00009379static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00009380changemail(const char *val ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +00009381{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009382 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009383}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009384
Denis Vlasenko131ae172007-02-18 13:00:19 +00009385#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009386
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009387
9388/* ============ ??? */
9389
Eric Andersencb57d552001-06-28 07:25:16 +00009390/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009391 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009392 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009393static void
9394setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009395{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009396 char **newparam;
9397 char **ap;
9398 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009399
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009400 for (nparam = 0; argv[nparam]; nparam++)
9401 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009402 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9403 while (*argv) {
9404 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009405 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009406 *ap = NULL;
9407 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009408 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009409 shellparam.nparam = nparam;
9410 shellparam.p = newparam;
9411#if ENABLE_ASH_GETOPTS
9412 shellparam.optind = 1;
9413 shellparam.optoff = -1;
9414#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009415}
9416
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009417/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009418 * Process shell options. The global variable argptr contains a pointer
9419 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009420 *
9421 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9422 * For a non-interactive shell, an error condition encountered
9423 * by a special built-in ... shall cause the shell to write a diagnostic message
9424 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009425 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009426 * ...
9427 * Utility syntax error (option or operand error) Shall exit
9428 * ...
9429 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9430 * we see that bash does not do that (set "finishes" with error code 1 instead,
9431 * and shell continues), and people rely on this behavior!
9432 * Testcase:
9433 * set -o barfoo 2>/dev/null
9434 * echo $?
9435 *
9436 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009437 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009438static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009439minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009440{
9441 int i;
9442
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009443 if (name) {
9444 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009445 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009447 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009448 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009449 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009450 ash_msg("illegal option -o %s", name);
9451 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009452 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009453 out1str("Current option settings\n");
9454 for (i = 0; i < NOPTS; i++)
9455 out1fmt("%-16s%s\n", optnames(i),
9456 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009457 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009458}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009459static void
9460setoption(int flag, int val)
9461{
9462 int i;
9463
9464 for (i = 0; i < NOPTS; i++) {
9465 if (optletters(i) == flag) {
9466 optlist[i] = val;
9467 return;
9468 }
9469 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009470 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009471 /* NOTREACHED */
9472}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009473static int
Eric Andersenc470f442003-07-28 09:56:35 +00009474options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009475{
9476 char *p;
9477 int val;
9478 int c;
9479
9480 if (cmdline)
9481 minusc = NULL;
9482 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009483 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009484 if (c != '-' && c != '+')
9485 break;
9486 argptr++;
9487 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009489 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009490 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009491 if (!cmdline) {
9492 /* "-" means turn off -x and -v */
9493 if (p[0] == '\0')
9494 xflag = vflag = 0;
9495 /* "--" means reset params */
9496 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009497 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009498 }
Eric Andersenc470f442003-07-28 09:56:35 +00009499 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009500 }
Eric Andersencb57d552001-06-28 07:25:16 +00009501 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009502 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009503 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009504 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009505 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009506 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009507 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009508 if (minus_o(*argptr, val)) {
9509 /* it already printed err message */
9510 return 1; /* error */
9511 }
Eric Andersencb57d552001-06-28 07:25:16 +00009512 if (*argptr)
9513 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009514 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9515 isloginsh = 1;
9516 /* bash does not accept +-login, we also won't */
9517 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009518 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009519 isloginsh = 1;
9520 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009521 } else {
9522 setoption(c, val);
9523 }
9524 }
9525 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009526 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009527}
9528
Eric Andersencb57d552001-06-28 07:25:16 +00009529/*
Eric Andersencb57d552001-06-28 07:25:16 +00009530 * The shift builtin command.
9531 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009532static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009533shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009534{
9535 int n;
9536 char **ap1, **ap2;
9537
9538 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009539 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009540 n = number(argv[1]);
9541 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009542 ash_msg_and_raise_error("can't shift that many");
9543 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009544 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009545 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009546 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009547 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009548 }
9549 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009550 while ((*ap2++ = *ap1++) != NULL)
9551 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009552#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009553 shellparam.optind = 1;
9554 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009555#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009556 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009557 return 0;
9558}
9559
Eric Andersencb57d552001-06-28 07:25:16 +00009560/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009561 * POSIX requires that 'set' (but not export or readonly) output the
9562 * variables in lexicographic order - by the locale's collating order (sigh).
9563 * Maybe we could keep them in an ordered balanced binary tree
9564 * instead of hashed lists.
9565 * For now just roll 'em through qsort for printing...
9566 */
9567static int
9568showvars(const char *sep_prefix, int on, int off)
9569{
9570 const char *sep;
9571 char **ep, **epend;
9572
9573 ep = listvars(on, off, &epend);
9574 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9575
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009576 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009577
9578 for (; ep < epend; ep++) {
9579 const char *p;
9580 const char *q;
9581
9582 p = strchrnul(*ep, '=');
9583 q = nullstr;
9584 if (*p)
9585 q = single_quote(++p);
9586 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9587 }
9588 return 0;
9589}
9590
9591/*
Eric Andersencb57d552001-06-28 07:25:16 +00009592 * The set command builtin.
9593 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009594static int
Denis Vlasenko68404f12008-03-17 09:00:54 +00009595setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +00009596{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009597 int retval;
9598
Denis Vlasenko68404f12008-03-17 09:00:54 +00009599 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009600 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009601 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009602 retval = 1;
9603 if (!options(0)) { /* if no parse error... */
9604 retval = 0;
9605 optschanged();
9606 if (*argptr != NULL) {
9607 setparam(argptr);
9608 }
Eric Andersencb57d552001-06-28 07:25:16 +00009609 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009610 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009611 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009612}
9613
Denis Vlasenko131ae172007-02-18 13:00:19 +00009614#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009615/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009616static void
9617change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009618{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009619 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009620 /* "get", generate */
9621 char buf[16];
9622
9623 rseed = rseed * 1103515245 + 12345;
9624 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9625 /* set without recursion */
9626 setvar(vrandom.text, buf, VNOFUNC);
9627 vrandom.flags &= ~VNOFUNC;
9628 } else {
9629 /* set/reset */
9630 rseed = strtoul(value, (char **)NULL, 10);
9631 }
Eric Andersenef02f822004-03-11 13:34:24 +00009632}
Eric Andersen16767e22004-03-16 05:14:10 +00009633#endif
9634
Denis Vlasenko131ae172007-02-18 13:00:19 +00009635#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009636static int
Eric Andersenc470f442003-07-28 09:56:35 +00009637getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009638{
9639 char *p, *q;
9640 char c = '?';
9641 int done = 0;
9642 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009643 char s[12];
9644 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009645
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009646 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009647 return 1;
9648 optnext = optfirst + *param_optind - 1;
9649
9650 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009651 p = NULL;
9652 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009653 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009654 if (p == NULL || *p == '\0') {
9655 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009656 p = *optnext;
9657 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009658 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009659 p = NULL;
9660 done = 1;
9661 goto out;
9662 }
9663 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009664 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009665 goto atend;
9666 }
9667
9668 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009669 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009670 if (*q == '\0') {
9671 if (optstr[0] == ':') {
9672 s[0] = c;
9673 s[1] = '\0';
9674 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009675 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009676 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009677 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009678 }
9679 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009680 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009681 }
9682 if (*++q == ':')
9683 q++;
9684 }
9685
9686 if (*++q == ':') {
9687 if (*p == '\0' && (p = *optnext) == NULL) {
9688 if (optstr[0] == ':') {
9689 s[0] = c;
9690 s[1] = '\0';
9691 err |= setvarsafe("OPTARG", s, 0);
9692 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009693 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009694 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009695 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009696 c = '?';
9697 }
Eric Andersenc470f442003-07-28 09:56:35 +00009698 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009699 }
9700
9701 if (p == *optnext)
9702 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009703 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009704 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009705 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009706 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009707 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009708 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009709 *param_optind = optnext - optfirst + 1;
9710 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009711 err |= setvarsafe("OPTIND", s, VNOFUNC);
9712 s[0] = c;
9713 s[1] = '\0';
9714 err |= setvarsafe(optvar, s, 0);
9715 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009716 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009717 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009718 flush_stdout_stderr();
9719 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009720 }
9721 return done;
9722}
Eric Andersenc470f442003-07-28 09:56:35 +00009723
9724/*
9725 * The getopts builtin. Shellparam.optnext points to the next argument
9726 * to be processed. Shellparam.optptr points to the next character to
9727 * be processed in the current argument. If shellparam.optnext is NULL,
9728 * then it's the first time getopts has been called.
9729 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009730static int
Eric Andersenc470f442003-07-28 09:56:35 +00009731getoptscmd(int argc, char **argv)
9732{
9733 char **optbase;
9734
9735 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009736 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009737 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009738 optbase = shellparam.p;
9739 if (shellparam.optind > shellparam.nparam + 1) {
9740 shellparam.optind = 1;
9741 shellparam.optoff = -1;
9742 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009743 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009744 optbase = &argv[3];
9745 if (shellparam.optind > argc - 2) {
9746 shellparam.optind = 1;
9747 shellparam.optoff = -1;
9748 }
9749 }
9750
9751 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009752 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009753}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009754#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009755
Eric Andersencb57d552001-06-28 07:25:16 +00009756
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009757/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009758
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009759/*
9760 * NEOF is returned by parsecmd when it encounters an end of file. It
9761 * must be distinct from NULL, so we use the address of a variable that
9762 * happens to be handy.
9763 */
9764static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009765#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009766static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009767static int lasttoken; /* last token read */
9768static char *wordtext; /* text of last word returned by readtoken */
9769static struct nodelist *backquotelist;
9770static union node *redirnode;
9771static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009772static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009773
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009774static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9775static void
9776raise_error_syntax(const char *msg)
9777{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009778 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009779 /* NOTREACHED */
9780}
9781
9782/*
9783 * Called when an unexpected token is read during the parse. The argument
9784 * is the token that is expected, or -1 if more than one type of token can
9785 * occur at this point.
9786 */
9787static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9788static void
9789raise_error_unexpected_syntax(int token)
9790{
9791 char msg[64];
9792 int l;
9793
9794 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9795 if (token >= 0)
9796 sprintf(msg + l, " (expecting %s)", tokname(token));
9797 raise_error_syntax(msg);
9798 /* NOTREACHED */
9799}
Eric Andersencb57d552001-06-28 07:25:16 +00009800
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009801#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009802
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009803struct heredoc {
9804 struct heredoc *next; /* next here document in list */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009805 union node *here; /* redirection node */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009806 char *eofmark; /* string indicating end of input */
9807 int striptabs; /* if set, strip leading tabs */
9808};
Eric Andersencb57d552001-06-28 07:25:16 +00009809
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009810static struct heredoc *heredoclist; /* list of here documents to read */
9811
9812/* parsing is heavily cross-recursive, need these forward decls */
9813static union node *andor(void);
9814static union node *pipeline(void);
9815static union node *parse_command(void);
9816static void parseheredoc(void);
9817static char peektoken(void);
9818static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009819
Eric Andersenc470f442003-07-28 09:56:35 +00009820static union node *
9821list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009822{
9823 union node *n1, *n2, *n3;
9824 int tok;
9825
Eric Andersenc470f442003-07-28 09:56:35 +00009826 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9827 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009828 return NULL;
9829 n1 = NULL;
9830 for (;;) {
9831 n2 = andor();
9832 tok = readtoken();
9833 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009834 if (n2->type == NPIPE) {
9835 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009836 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009837 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009838 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009839 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009840 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009841 n2 = n3;
9842 }
9843 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009844 }
9845 }
9846 if (n1 == NULL) {
9847 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009848 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009849 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009850 n3->type = NSEMI;
9851 n3->nbinary.ch1 = n1;
9852 n3->nbinary.ch2 = n2;
9853 n1 = n3;
9854 }
9855 switch (tok) {
9856 case TBACKGND:
9857 case TSEMI:
9858 tok = readtoken();
9859 /* fall through */
9860 case TNL:
9861 if (tok == TNL) {
9862 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009863 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009864 return n1;
9865 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009866 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009867 }
Eric Andersenc470f442003-07-28 09:56:35 +00009868 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009869 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009870 return n1;
9871 break;
9872 case TEOF:
9873 if (heredoclist)
9874 parseheredoc();
9875 else
Eric Andersenc470f442003-07-28 09:56:35 +00009876 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009877 return n1;
9878 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009879 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009880 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009881 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009882 return n1;
9883 }
9884 }
9885}
9886
Eric Andersenc470f442003-07-28 09:56:35 +00009887static union node *
9888andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009889{
Eric Andersencb57d552001-06-28 07:25:16 +00009890 union node *n1, *n2, *n3;
9891 int t;
9892
Eric Andersencb57d552001-06-28 07:25:16 +00009893 n1 = pipeline();
9894 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009895 t = readtoken();
9896 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009897 t = NAND;
9898 } else if (t == TOR) {
9899 t = NOR;
9900 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009901 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009902 return n1;
9903 }
Eric Andersenc470f442003-07-28 09:56:35 +00009904 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009905 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009906 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009907 n3->type = t;
9908 n3->nbinary.ch1 = n1;
9909 n3->nbinary.ch2 = n2;
9910 n1 = n3;
9911 }
9912}
9913
Eric Andersenc470f442003-07-28 09:56:35 +00009914static union node *
9915pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009916{
Eric Andersencb57d552001-06-28 07:25:16 +00009917 union node *n1, *n2, *pipenode;
9918 struct nodelist *lp, *prev;
9919 int negate;
9920
9921 negate = 0;
9922 TRACE(("pipeline: entered\n"));
9923 if (readtoken() == TNOT) {
9924 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009925 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009926 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009927 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009928 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009929 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009930 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009931 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009932 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009933 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009934 pipenode->npipe.cmdlist = lp;
9935 lp->n = n1;
9936 do {
9937 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009938 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009939 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009940 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009941 prev->next = lp;
9942 } while (readtoken() == TPIPE);
9943 lp->next = NULL;
9944 n1 = pipenode;
9945 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009946 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009947 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009948 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009949 n2->type = NNOT;
9950 n2->nnot.com = n1;
9951 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009952 }
9953 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009954}
9955
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009956static union node *
9957makename(void)
9958{
9959 union node *n;
9960
Denis Vlasenko597906c2008-02-20 16:38:54 +00009961 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009962 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009963 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009964 n->narg.text = wordtext;
9965 n->narg.backquote = backquotelist;
9966 return n;
9967}
9968
9969static void
9970fixredir(union node *n, const char *text, int err)
9971{
9972 TRACE(("Fix redir %s %d\n", text, err));
9973 if (!err)
9974 n->ndup.vname = NULL;
9975
9976 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009977 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009978 else if (LONE_DASH(text))
9979 n->ndup.dupfd = -1;
9980 else {
9981 if (err)
9982 raise_error_syntax("Bad fd number");
9983 n->ndup.vname = makename();
9984 }
9985}
9986
9987/*
9988 * Returns true if the text contains nothing to expand (no dollar signs
9989 * or backquotes).
9990 */
9991static int
9992noexpand(char *text)
9993{
9994 char *p;
9995 char c;
9996
9997 p = text;
9998 while ((c = *p++) != '\0') {
9999 if (c == CTLQUOTEMARK)
10000 continue;
10001 if (c == CTLESC)
10002 p++;
10003 else if (SIT(c, BASESYNTAX) == CCTL)
10004 return 0;
10005 }
10006 return 1;
10007}
10008
10009static void
10010parsefname(void)
10011{
10012 union node *n = redirnode;
10013
10014 if (readtoken() != TWORD)
10015 raise_error_unexpected_syntax(-1);
10016 if (n->type == NHERE) {
10017 struct heredoc *here = heredoc;
10018 struct heredoc *p;
10019 int i;
10020
10021 if (quoteflag == 0)
10022 n->type = NXHERE;
10023 TRACE(("Here document %d\n", n->type));
10024 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
10025 raise_error_syntax("Illegal eof marker for << redirection");
10026 rmescapes(wordtext);
10027 here->eofmark = wordtext;
10028 here->next = NULL;
10029 if (heredoclist == NULL)
10030 heredoclist = here;
10031 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010032 for (p = heredoclist; p->next; p = p->next)
10033 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010034 p->next = here;
10035 }
10036 } else if (n->type == NTOFD || n->type == NFROMFD) {
10037 fixredir(n, wordtext, 0);
10038 } else {
10039 n->nfile.fname = makename();
10040 }
10041}
Eric Andersencb57d552001-06-28 07:25:16 +000010042
Eric Andersenc470f442003-07-28 09:56:35 +000010043static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010044simplecmd(void)
10045{
10046 union node *args, **app;
10047 union node *n = NULL;
10048 union node *vars, **vpp;
10049 union node **rpp, *redir;
10050 int savecheckkwd;
10051
10052 args = NULL;
10053 app = &args;
10054 vars = NULL;
10055 vpp = &vars;
10056 redir = NULL;
10057 rpp = &redir;
10058
10059 savecheckkwd = CHKALIAS;
10060 for (;;) {
10061 checkkwd = savecheckkwd;
10062 switch (readtoken()) {
10063 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010064 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010065 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010066 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010067 n->narg.text = wordtext;
10068 n->narg.backquote = backquotelist;
10069 if (savecheckkwd && isassignment(wordtext)) {
10070 *vpp = n;
10071 vpp = &n->narg.next;
10072 } else {
10073 *app = n;
10074 app = &n->narg.next;
10075 savecheckkwd = 0;
10076 }
10077 break;
10078 case TREDIR:
10079 *rpp = n = redirnode;
10080 rpp = &n->nfile.next;
10081 parsefname(); /* read name of redirection file */
10082 break;
10083 case TLP:
10084 if (args && app == &args->narg.next
10085 && !vars && !redir
10086 ) {
10087 struct builtincmd *bcmd;
10088 const char *name;
10089
10090 /* We have a function */
10091 if (readtoken() != TRP)
10092 raise_error_unexpected_syntax(TRP);
10093 name = n->narg.text;
10094 if (!goodname(name)
10095 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10096 ) {
10097 raise_error_syntax("Bad function name");
10098 }
10099 n->type = NDEFUN;
10100 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10101 n->narg.next = parse_command();
10102 return n;
10103 }
10104 /* fall through */
10105 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010106 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010107 goto out;
10108 }
10109 }
10110 out:
10111 *app = NULL;
10112 *vpp = NULL;
10113 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010114 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010115 n->type = NCMD;
10116 n->ncmd.args = args;
10117 n->ncmd.assign = vars;
10118 n->ncmd.redirect = redir;
10119 return n;
10120}
10121
10122static union node *
10123parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010124{
Eric Andersencb57d552001-06-28 07:25:16 +000010125 union node *n1, *n2;
10126 union node *ap, **app;
10127 union node *cp, **cpp;
10128 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010129 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010130 int t;
10131
10132 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010133 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010134
Eric Andersencb57d552001-06-28 07:25:16 +000010135 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010136 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010137 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010138 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010139 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010140 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010141 n1->type = NIF;
10142 n1->nif.test = list(0);
10143 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010144 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010145 n1->nif.ifpart = list(0);
10146 n2 = n1;
10147 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010148 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010149 n2 = n2->nif.elsepart;
10150 n2->type = NIF;
10151 n2->nif.test = list(0);
10152 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010153 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010154 n2->nif.ifpart = list(0);
10155 }
10156 if (lasttoken == TELSE)
10157 n2->nif.elsepart = list(0);
10158 else {
10159 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010160 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010161 }
Eric Andersenc470f442003-07-28 09:56:35 +000010162 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010163 break;
10164 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010165 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010166 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010167 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010168 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010169 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010170 got = readtoken();
10171 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010172 TRACE(("expecting DO got %s %s\n", tokname(got),
10173 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010174 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010175 }
10176 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010177 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010178 break;
10179 }
10180 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +000010181 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010182 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010183 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010184 n1->type = NFOR;
10185 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010186 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010187 if (readtoken() == TIN) {
10188 app = &ap;
10189 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010190 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010191 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010192 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010193 n2->narg.text = wordtext;
10194 n2->narg.backquote = backquotelist;
10195 *app = n2;
10196 app = &n2->narg.next;
10197 }
10198 *app = NULL;
10199 n1->nfor.args = ap;
10200 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010201 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010202 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010203 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010204 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010205 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010206 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010207 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010208 n1->nfor.args = n2;
10209 /*
10210 * Newline or semicolon here is optional (but note
10211 * that the original Bourne shell only allowed NL).
10212 */
10213 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010214 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010215 }
Eric Andersenc470f442003-07-28 09:56:35 +000010216 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010217 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010218 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010219 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010220 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010221 break;
10222 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010223 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010224 n1->type = NCASE;
10225 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010226 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010227 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010228 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010229 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010230 n2->narg.text = wordtext;
10231 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010232 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010233 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010234 } while (readtoken() == TNL);
10235 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010236 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010237 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010238 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010239 checkkwd = CHKNL | CHKKWD;
10240 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010241 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010242 if (lasttoken == TLP)
10243 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010244 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010245 cp->type = NCLIST;
10246 app = &cp->nclist.pattern;
10247 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010248 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010249 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010250 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010251 ap->narg.text = wordtext;
10252 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010253 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010254 break;
10255 app = &ap->narg.next;
10256 readtoken();
10257 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010258 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010259 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010260 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010261 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010262
Eric Andersenc470f442003-07-28 09:56:35 +000010263 cpp = &cp->nclist.next;
10264
10265 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010266 t = readtoken();
10267 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010268 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010269 raise_error_unexpected_syntax(TENDCASE);
10270 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010271 }
Eric Andersenc470f442003-07-28 09:56:35 +000010272 }
Eric Andersencb57d552001-06-28 07:25:16 +000010273 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010274 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010275 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010276 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010277 n1->type = NSUBSHELL;
10278 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010279 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010280 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 break;
10282 case TBEGIN:
10283 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010284 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010285 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010286 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010287 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010288 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010289 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010290 }
10291
Eric Andersenc470f442003-07-28 09:56:35 +000010292 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010293 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010294
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010295 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010296 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010297 checkkwd = CHKKWD | CHKALIAS;
10298 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010299 while (readtoken() == TREDIR) {
10300 *rpp = n2 = redirnode;
10301 rpp = &n2->nfile.next;
10302 parsefname();
10303 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010304 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010305 *rpp = NULL;
10306 if (redir) {
10307 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010308 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010309 n2->type = NREDIR;
10310 n2->nredir.n = n1;
10311 n1 = n2;
10312 }
10313 n1->nredir.redirect = redir;
10314 }
Eric Andersencb57d552001-06-28 07:25:16 +000010315 return n1;
10316}
10317
Eric Andersencb57d552001-06-28 07:25:16 +000010318/*
10319 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10320 * is not NULL, read a here document. In the latter case, eofmark is the
10321 * word which marks the end of the document and striptabs is true if
10322 * leading tabs should be stripped from the document. The argument firstc
10323 * is the first character of the input token or document.
10324 *
10325 * Because C does not have internal subroutines, I have simulated them
10326 * using goto's to implement the subroutine linkage. The following macros
10327 * will run code that appears at the end of readtoken1.
10328 */
10329
Eric Andersen2870d962001-07-02 17:27:21 +000010330#define CHECKEND() {goto checkend; checkend_return:;}
10331#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10332#define PARSESUB() {goto parsesub; parsesub_return:;}
10333#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10334#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10335#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010336
10337static int
Eric Andersenc470f442003-07-28 09:56:35 +000010338readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010339{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010340 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010341 int c = firstc;
10342 char *out;
10343 int len;
10344 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010345 struct nodelist *bqlist;
10346 smallint quotef;
10347 smallint dblquote;
10348 smallint oldstyle;
10349 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010350#if ENABLE_ASH_EXPAND_PRMT
10351 smallint pssyntax; /* we are expanding a prompt string */
10352#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010353 int varnest; /* levels of variables expansion */
10354 int arinest; /* levels of arithmetic expansion */
10355 int parenlevel; /* levels of parens in arithmetic */
10356 int dqvarnest; /* levels of variables expansion within double quotes */
10357
Eric Andersencb57d552001-06-28 07:25:16 +000010358#if __GNUC__
10359 /* Avoid longjmp clobbering */
10360 (void) &out;
10361 (void) &quotef;
10362 (void) &dblquote;
10363 (void) &varnest;
10364 (void) &arinest;
10365 (void) &parenlevel;
10366 (void) &dqvarnest;
10367 (void) &oldstyle;
10368 (void) &prevsyntax;
10369 (void) &syntax;
10370#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010371 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010372 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010373 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010374 oldstyle = 0;
10375 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010376#if ENABLE_ASH_EXPAND_PRMT
10377 pssyntax = (syntax == PSSYNTAX);
10378 if (pssyntax)
10379 syntax = DQSYNTAX;
10380#endif
10381 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010382 varnest = 0;
10383 arinest = 0;
10384 parenlevel = 0;
10385 dqvarnest = 0;
10386
10387 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010388 loop: { /* for each line, until end of word */
10389 CHECKEND(); /* set c to PEOF if at end of here document */
10390 for (;;) { /* until end of line or end of word */
10391 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010392 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010393 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010394 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010395 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010396 USTPUTC(c, out);
10397 plinno++;
10398 if (doprompt)
10399 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010400 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010401 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010402 case CWORD:
10403 USTPUTC(c, out);
10404 break;
10405 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010406 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010407 USTPUTC(CTLESC, out);
10408 USTPUTC(c, out);
10409 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010410 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010411 c = pgetc2();
10412 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010413 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010414 USTPUTC('\\', out);
10415 pungetc();
10416 } else if (c == '\n') {
10417 if (doprompt)
10418 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010419 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010420#if ENABLE_ASH_EXPAND_PRMT
10421 if (c == '$' && pssyntax) {
10422 USTPUTC(CTLESC, out);
10423 USTPUTC('\\', out);
10424 }
10425#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010426 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010427 c != '\\' && c != '`' &&
10428 c != '$' && (
10429 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010430 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010431 ) {
10432 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010433 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010434 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010435 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010436 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010437 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010438 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010439 }
10440 break;
10441 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010442 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010443 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010444 if (eofmark == NULL) {
10445 USTPUTC(CTLQUOTEMARK, out);
10446 }
Eric Andersencb57d552001-06-28 07:25:16 +000010447 break;
10448 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010449 syntax = DQSYNTAX;
10450 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010451 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010452 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010453 if (eofmark != NULL && arinest == 0
10454 && varnest == 0
10455 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010456 USTPUTC(c, out);
10457 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010458 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010459 syntax = BASESYNTAX;
10460 dblquote = 0;
10461 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010462 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010463 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010464 }
10465 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010466 case CVAR: /* '$' */
10467 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010468 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010469 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010470 if (varnest > 0) {
10471 varnest--;
10472 if (dqvarnest > 0) {
10473 dqvarnest--;
10474 }
10475 USTPUTC(CTLENDVAR, out);
10476 } else {
10477 USTPUTC(c, out);
10478 }
10479 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010480#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010481 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010482 parenlevel++;
10483 USTPUTC(c, out);
10484 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010485 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010486 if (parenlevel > 0) {
10487 USTPUTC(c, out);
10488 --parenlevel;
10489 } else {
10490 if (pgetc() == ')') {
10491 if (--arinest == 0) {
10492 USTPUTC(CTLENDARI, out);
10493 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010494 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010495 } else
10496 USTPUTC(')', out);
10497 } else {
10498 /*
10499 * unbalanced parens
10500 * (don't 2nd guess - no error)
10501 */
10502 pungetc();
10503 USTPUTC(')', out);
10504 }
10505 }
10506 break;
10507#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010508 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010509 PARSEBACKQOLD();
10510 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010511 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010512 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010513 case CIGN:
10514 break;
10515 default:
10516 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010517 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010518#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010519 if (c != PEOA)
10520#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010521 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010522
Eric Andersencb57d552001-06-28 07:25:16 +000010523 }
10524 c = pgetc_macro();
10525 }
10526 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010527 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010528#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010529 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010530 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010531#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010532 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010533 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010534 if (varnest != 0) {
10535 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010536 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010537 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010538 }
10539 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010540 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010541 out = stackblock();
10542 if (eofmark == NULL) {
10543 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010544 && quotef == 0
10545 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010546 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010547 PARSEREDIR();
10548 return lasttoken = TREDIR;
10549 } else {
10550 pungetc();
10551 }
10552 }
10553 quoteflag = quotef;
10554 backquotelist = bqlist;
10555 grabstackblock(len);
10556 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010557 lasttoken = TWORD;
10558 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010559/* end of readtoken routine */
10560
Eric Andersencb57d552001-06-28 07:25:16 +000010561/*
10562 * Check to see whether we are at the end of the here document. When this
10563 * is called, c is set to the first character of the next input line. If
10564 * we are at the end of the here document, this routine sets the c to PEOF.
10565 */
Eric Andersenc470f442003-07-28 09:56:35 +000010566checkend: {
10567 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010568#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010569 if (c == PEOA) {
10570 c = pgetc2();
10571 }
10572#endif
10573 if (striptabs) {
10574 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010575 c = pgetc2();
10576 }
Eric Andersenc470f442003-07-28 09:56:35 +000010577 }
10578 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010579 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010580 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010581
Eric Andersenc470f442003-07-28 09:56:35 +000010582 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010583 for (q = eofmark + 1; *q && *p == *q; p++, q++)
10584 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000010585 if (*p == '\n' && *q == '\0') {
10586 c = PEOF;
10587 plinno++;
10588 needprompt = doprompt;
10589 } else {
10590 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010591 }
10592 }
10593 }
10594 }
Eric Andersenc470f442003-07-28 09:56:35 +000010595 goto checkend_return;
10596}
Eric Andersencb57d552001-06-28 07:25:16 +000010597
Eric Andersencb57d552001-06-28 07:25:16 +000010598/*
10599 * Parse a redirection operator. The variable "out" points to a string
10600 * specifying the fd to be redirected. The variable "c" contains the
10601 * first character of the redirection operator.
10602 */
Eric Andersenc470f442003-07-28 09:56:35 +000010603parseredir: {
10604 char fd = *out;
10605 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010606
Denis Vlasenko597906c2008-02-20 16:38:54 +000010607 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010608 if (c == '>') {
10609 np->nfile.fd = 1;
10610 c = pgetc();
10611 if (c == '>')
10612 np->type = NAPPEND;
10613 else if (c == '|')
10614 np->type = NCLOBBER;
10615 else if (c == '&')
10616 np->type = NTOFD;
10617 else {
10618 np->type = NTO;
10619 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010620 }
Eric Andersenc470f442003-07-28 09:56:35 +000010621 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010622 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010623 c = pgetc();
10624 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010625 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010626 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010627 np = stzalloc(sizeof(struct nhere));
10628 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010629 }
10630 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010631 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010632 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010633 c = pgetc();
10634 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010635 heredoc->striptabs = 1;
10636 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010637 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010638 pungetc();
10639 }
10640 break;
10641
10642 case '&':
10643 np->type = NFROMFD;
10644 break;
10645
10646 case '>':
10647 np->type = NFROMTO;
10648 break;
10649
10650 default:
10651 np->type = NFROM;
10652 pungetc();
10653 break;
10654 }
Eric Andersencb57d552001-06-28 07:25:16 +000010655 }
Eric Andersenc470f442003-07-28 09:56:35 +000010656 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010657 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010658 redirnode = np;
10659 goto parseredir_return;
10660}
Eric Andersencb57d552001-06-28 07:25:16 +000010661
Eric Andersencb57d552001-06-28 07:25:16 +000010662/*
10663 * Parse a substitution. At this point, we have read the dollar sign
10664 * and nothing else.
10665 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010666
10667/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10668 * (assuming ascii char codes, as the original implementation did) */
10669#define is_special(c) \
10670 ((((unsigned int)c) - 33 < 32) \
10671 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010672parsesub: {
10673 int subtype;
10674 int typeloc;
10675 int flags;
10676 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010677 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010678
Eric Andersenc470f442003-07-28 09:56:35 +000010679 c = pgetc();
10680 if (
10681 c <= PEOA_OR_PEOF ||
10682 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10683 ) {
10684 USTPUTC('$', out);
10685 pungetc();
10686 } else if (c == '(') { /* $(command) or $((arith)) */
10687 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010688#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010689 PARSEARITH();
10690#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010691 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010692#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010693 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010694 pungetc();
10695 PARSEBACKQNEW();
10696 }
10697 } else {
10698 USTPUTC(CTLVAR, out);
10699 typeloc = out - (char *)stackblock();
10700 USTPUTC(VSNORMAL, out);
10701 subtype = VSNORMAL;
10702 if (c == '{') {
10703 c = pgetc();
10704 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010705 c = pgetc();
10706 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010707 c = '#';
10708 else
10709 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010710 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010711 subtype = 0;
10712 }
10713 if (c > PEOA_OR_PEOF && is_name(c)) {
10714 do {
10715 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010716 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010717 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010718 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010719 do {
10720 STPUTC(c, out);
10721 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010722 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010723 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010724 USTPUTC(c, out);
10725 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010726 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010727 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010728
Eric Andersenc470f442003-07-28 09:56:35 +000010729 STPUTC('=', out);
10730 flags = 0;
10731 if (subtype == 0) {
10732 switch (c) {
10733 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000010734 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010735#if ENABLE_ASH_BASH_COMPAT
10736 if (c == ':' || c == '$' || isdigit(c)) {
10737 pungetc();
10738 subtype = VSSUBSTR;
10739 break;
10740 }
10741#endif
10742 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000010743 /*FALLTHROUGH*/
10744 default:
10745 p = strchr(types, c);
10746 if (p == NULL)
10747 goto badsub;
10748 subtype = p - types + VSNORMAL;
10749 break;
10750 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000010751 case '#': {
10752 int cc = c;
10753 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
10754 c = pgetc();
10755 if (c == cc)
10756 subtype++;
10757 else
10758 pungetc();
10759 break;
10760 }
10761#if ENABLE_ASH_BASH_COMPAT
10762 case '/':
10763 subtype = VSREPLACE;
10764 c = pgetc();
10765 if (c == '/')
10766 subtype++; /* VSREPLACEALL */
10767 else
10768 pungetc();
10769 break;
10770#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010771 }
Eric Andersenc470f442003-07-28 09:56:35 +000010772 } else {
10773 pungetc();
10774 }
10775 if (dblquote || arinest)
10776 flags |= VSQUOTE;
10777 *((char *)stackblock() + typeloc) = subtype | flags;
10778 if (subtype != VSNORMAL) {
10779 varnest++;
10780 if (dblquote || arinest) {
10781 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010782 }
10783 }
10784 }
Eric Andersenc470f442003-07-28 09:56:35 +000010785 goto parsesub_return;
10786}
Eric Andersencb57d552001-06-28 07:25:16 +000010787
Eric Andersencb57d552001-06-28 07:25:16 +000010788/*
10789 * Called to parse command substitutions. Newstyle is set if the command
10790 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10791 * list of commands (passed by reference), and savelen is the number of
10792 * characters on the top of the stack which must be preserved.
10793 */
Eric Andersenc470f442003-07-28 09:56:35 +000010794parsebackq: {
10795 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010796 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010797 union node *n;
10798 char *volatile str;
10799 struct jmploc jmploc;
10800 struct jmploc *volatile savehandler;
10801 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010802 smallint saveprompt = 0;
10803
Eric Andersencb57d552001-06-28 07:25:16 +000010804#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010805 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010806#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010807 savepbq = parsebackquote;
10808 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010809 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010810 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010811 exception_handler = savehandler;
10812 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010813 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010814 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010815 str = NULL;
10816 savelen = out - (char *)stackblock();
10817 if (savelen > 0) {
10818 str = ckmalloc(savelen);
10819 memcpy(str, stackblock(), savelen);
10820 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010821 savehandler = exception_handler;
10822 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010823 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010824 if (oldstyle) {
10825 /* We must read until the closing backquote, giving special
10826 treatment to some slashes, and then push the string and
10827 reread it as input, interpreting it normally. */
10828 char *pout;
10829 int pc;
10830 size_t psavelen;
10831 char *pstr;
10832
10833
10834 STARTSTACKSTR(pout);
10835 for (;;) {
10836 if (needprompt) {
10837 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010838 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010839 pc = pgetc();
10840 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010841 case '`':
10842 goto done;
10843
10844 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010845 pc = pgetc();
10846 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010847 plinno++;
10848 if (doprompt)
10849 setprompt(2);
10850 /*
10851 * If eating a newline, avoid putting
10852 * the newline into the new character
10853 * stream (via the STPUTC after the
10854 * switch).
10855 */
10856 continue;
10857 }
10858 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010859 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010860 STPUTC('\\', pout);
10861 if (pc > PEOA_OR_PEOF) {
10862 break;
10863 }
10864 /* fall through */
10865
10866 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010867#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010868 case PEOA:
10869#endif
10870 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010871 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010872
10873 case '\n':
10874 plinno++;
10875 needprompt = doprompt;
10876 break;
10877
10878 default:
10879 break;
10880 }
10881 STPUTC(pc, pout);
10882 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010883 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010884 STPUTC('\0', pout);
10885 psavelen = pout - (char *)stackblock();
10886 if (psavelen > 0) {
10887 pstr = grabstackstr(pout);
10888 setinputstring(pstr);
10889 }
10890 }
10891 nlpp = &bqlist;
10892 while (*nlpp)
10893 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010894 *nlpp = stzalloc(sizeof(**nlpp));
10895 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010896 parsebackquote = oldstyle;
10897
10898 if (oldstyle) {
10899 saveprompt = doprompt;
10900 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010901 }
10902
Eric Andersenc470f442003-07-28 09:56:35 +000010903 n = list(2);
10904
10905 if (oldstyle)
10906 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010907 else if (readtoken() != TRP)
10908 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010909
10910 (*nlpp)->n = n;
10911 if (oldstyle) {
10912 /*
10913 * Start reading from old file again, ignoring any pushed back
10914 * tokens left from the backquote parsing
10915 */
10916 popfile();
10917 tokpushback = 0;
10918 }
10919 while (stackblocksize() <= savelen)
10920 growstackblock();
10921 STARTSTACKSTR(out);
10922 if (str) {
10923 memcpy(out, str, savelen);
10924 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010925 INT_OFF;
10926 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010927 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010928 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010929 }
10930 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010931 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010932 if (arinest || dblquote)
10933 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10934 else
10935 USTPUTC(CTLBACKQ, out);
10936 if (oldstyle)
10937 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010938 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010939}
10940
Denis Vlasenko131ae172007-02-18 13:00:19 +000010941#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010942/*
10943 * Parse an arithmetic expansion (indicate start of one and set state)
10944 */
Eric Andersenc470f442003-07-28 09:56:35 +000010945parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010946 if (++arinest == 1) {
10947 prevsyntax = syntax;
10948 syntax = ARISYNTAX;
10949 USTPUTC(CTLARI, out);
10950 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010951 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010952 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010953 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010954 } else {
10955 /*
10956 * we collapse embedded arithmetic expansion to
10957 * parenthesis, which should be equivalent
10958 */
10959 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010960 }
Eric Andersenc470f442003-07-28 09:56:35 +000010961 goto parsearith_return;
10962}
10963#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010964
Eric Andersenc470f442003-07-28 09:56:35 +000010965} /* end of readtoken */
10966
Eric Andersencb57d552001-06-28 07:25:16 +000010967/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010968 * Read the next input token.
10969 * If the token is a word, we set backquotelist to the list of cmds in
10970 * backquotes. We set quoteflag to true if any part of the word was
10971 * quoted.
10972 * If the token is TREDIR, then we set redirnode to a structure containing
10973 * the redirection.
10974 * In all cases, the variable startlinno is set to the number of the line
10975 * on which the token starts.
10976 *
10977 * [Change comment: here documents and internal procedures]
10978 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10979 * word parsing code into a separate routine. In this case, readtoken
10980 * doesn't need to have any internal procedures, but parseword does.
10981 * We could also make parseoperator in essence the main routine, and
10982 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010983 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010984#define NEW_xxreadtoken
10985#ifdef NEW_xxreadtoken
10986/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010987static const char xxreadtoken_chars[7] ALIGN1 = {
10988 '\n', '(', ')', '&', '|', ';', 0
10989};
Eric Andersencb57d552001-06-28 07:25:16 +000010990
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010991static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010992 TNL, TLP, TRP, /* only single occurrence allowed */
10993 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10994 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010995 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010996};
10997
10998#define xxreadtoken_doubles \
10999 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
11000#define xxreadtoken_singles \
11001 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
11002
11003static int
11004xxreadtoken(void)
11005{
11006 int c;
11007
11008 if (tokpushback) {
11009 tokpushback = 0;
11010 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011011 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011012 if (needprompt) {
11013 setprompt(2);
11014 }
11015 startlinno = plinno;
11016 for (;;) { /* until token or start of word found */
11017 c = pgetc_macro();
11018
11019 if ((c != ' ') && (c != '\t')
11020#if ENABLE_ASH_ALIAS
11021 && (c != PEOA)
11022#endif
11023 ) {
11024 if (c == '#') {
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011025 while ((c = pgetc()) != '\n' && c != PEOF)
11026 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011027 pungetc();
11028 } else if (c == '\\') {
11029 if (pgetc() != '\n') {
11030 pungetc();
11031 goto READTOKEN1;
11032 }
11033 startlinno = ++plinno;
11034 if (doprompt)
11035 setprompt(2);
11036 } else {
11037 const char *p
11038 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11039
11040 if (c != PEOF) {
11041 if (c == '\n') {
11042 plinno++;
11043 needprompt = doprompt;
11044 }
11045
11046 p = strchr(xxreadtoken_chars, c);
11047 if (p == NULL) {
11048 READTOKEN1:
11049 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
11050 }
11051
11052 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
11053 if (pgetc() == *p) { /* double occurrence? */
11054 p += xxreadtoken_doubles + 1;
11055 } else {
11056 pungetc();
11057 }
11058 }
11059 }
11060 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11061 }
11062 }
11063 } /* for */
11064}
11065#else
11066#define RETURN(token) return lasttoken = token
11067static int
11068xxreadtoken(void)
11069{
11070 int c;
11071
11072 if (tokpushback) {
11073 tokpushback = 0;
11074 return lasttoken;
11075 }
11076 if (needprompt) {
11077 setprompt(2);
11078 }
11079 startlinno = plinno;
11080 for (;;) { /* until token or start of word found */
11081 c = pgetc_macro();
11082 switch (c) {
11083 case ' ': case '\t':
11084#if ENABLE_ASH_ALIAS
11085 case PEOA:
11086#endif
11087 continue;
11088 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011089 while ((c = pgetc()) != '\n' && c != PEOF)
11090 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011091 pungetc();
11092 continue;
11093 case '\\':
11094 if (pgetc() == '\n') {
11095 startlinno = ++plinno;
11096 if (doprompt)
11097 setprompt(2);
11098 continue;
11099 }
11100 pungetc();
11101 goto breakloop;
11102 case '\n':
11103 plinno++;
11104 needprompt = doprompt;
11105 RETURN(TNL);
11106 case PEOF:
11107 RETURN(TEOF);
11108 case '&':
11109 if (pgetc() == '&')
11110 RETURN(TAND);
11111 pungetc();
11112 RETURN(TBACKGND);
11113 case '|':
11114 if (pgetc() == '|')
11115 RETURN(TOR);
11116 pungetc();
11117 RETURN(TPIPE);
11118 case ';':
11119 if (pgetc() == ';')
11120 RETURN(TENDCASE);
11121 pungetc();
11122 RETURN(TSEMI);
11123 case '(':
11124 RETURN(TLP);
11125 case ')':
11126 RETURN(TRP);
11127 default:
11128 goto breakloop;
11129 }
11130 }
11131 breakloop:
11132 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11133#undef RETURN
11134}
11135#endif /* NEW_xxreadtoken */
11136
11137static int
11138readtoken(void)
11139{
11140 int t;
11141#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011142 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011143#endif
11144
11145#if ENABLE_ASH_ALIAS
11146 top:
11147#endif
11148
11149 t = xxreadtoken();
11150
11151 /*
11152 * eat newlines
11153 */
11154 if (checkkwd & CHKNL) {
11155 while (t == TNL) {
11156 parseheredoc();
11157 t = xxreadtoken();
11158 }
11159 }
11160
11161 if (t != TWORD || quoteflag) {
11162 goto out;
11163 }
11164
11165 /*
11166 * check for keywords
11167 */
11168 if (checkkwd & CHKKWD) {
11169 const char *const *pp;
11170
11171 pp = findkwd(wordtext);
11172 if (pp) {
11173 lasttoken = t = pp - tokname_array;
11174 TRACE(("keyword %s recognized\n", tokname(t)));
11175 goto out;
11176 }
11177 }
11178
11179 if (checkkwd & CHKALIAS) {
11180#if ENABLE_ASH_ALIAS
11181 struct alias *ap;
11182 ap = lookupalias(wordtext, 1);
11183 if (ap != NULL) {
11184 if (*ap->val) {
11185 pushstring(ap->val, ap);
11186 }
11187 goto top;
11188 }
11189#endif
11190 }
11191 out:
11192 checkkwd = 0;
11193#if DEBUG
11194 if (!alreadyseen)
11195 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11196 else
11197 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11198#endif
11199 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011200}
11201
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011202static char
11203peektoken(void)
11204{
11205 int t;
11206
11207 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011208 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011209 return tokname_array[t][0];
11210}
Eric Andersencb57d552001-06-28 07:25:16 +000011211
11212/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011213 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11214 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011215 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011216static union node *
11217parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011218{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011219 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011220
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011221 tokpushback = 0;
11222 doprompt = interact;
11223 if (doprompt)
11224 setprompt(doprompt);
11225 needprompt = 0;
11226 t = readtoken();
11227 if (t == TEOF)
11228 return NEOF;
11229 if (t == TNL)
11230 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011231 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011232 return list(1);
11233}
11234
11235/*
11236 * Input any here documents.
11237 */
11238static void
11239parseheredoc(void)
11240{
11241 struct heredoc *here;
11242 union node *n;
11243
11244 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011245 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011246
11247 while (here) {
11248 if (needprompt) {
11249 setprompt(2);
11250 }
11251 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11252 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011253 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011254 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011255 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011256 n->narg.text = wordtext;
11257 n->narg.backquote = backquotelist;
11258 here->here->nhere.doc = n;
11259 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011260 }
Eric Andersencb57d552001-06-28 07:25:16 +000011261}
11262
11263
11264/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011265 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011266 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011267#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011268static const char *
11269expandstr(const char *ps)
11270{
11271 union node n;
11272
11273 /* XXX Fix (char *) cast. */
11274 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011275 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011276 popfile();
11277
11278 n.narg.type = NARG;
11279 n.narg.next = NULL;
11280 n.narg.text = wordtext;
11281 n.narg.backquote = backquotelist;
11282
11283 expandarg(&n, NULL, 0);
11284 return stackblock();
11285}
11286#endif
11287
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011288/*
11289 * Execute a command or commands contained in a string.
11290 */
11291static int
11292evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011293{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011294 union node *n;
11295 struct stackmark smark;
11296 int skip;
11297
11298 setinputstring(s);
11299 setstackmark(&smark);
11300
11301 skip = 0;
11302 while ((n = parsecmd(0)) != NEOF) {
11303 evaltree(n, 0);
11304 popstackmark(&smark);
11305 skip = evalskip;
11306 if (skip)
11307 break;
11308 }
11309 popfile();
11310
11311 skip &= mask;
11312 evalskip = skip;
11313 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011314}
11315
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011316/*
11317 * The eval command.
11318 */
11319static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011320evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011321{
11322 char *p;
11323 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011324
Denis Vlasenko68404f12008-03-17 09:00:54 +000011325 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011326 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011327 argv += 2;
11328 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011329 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011330 for (;;) {
11331 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011332 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011333 if (p == NULL)
11334 break;
11335 STPUTC(' ', concat);
11336 }
11337 STPUTC('\0', concat);
11338 p = grabstackstr(concat);
11339 }
11340 evalstring(p, ~SKIPEVAL);
11341
11342 }
11343 return exitstatus;
11344}
11345
11346/*
11347 * Read and execute commands. "Top" is nonzero for the top level command
11348 * loop; it turns on prompting if the shell is interactive.
11349 */
11350static int
11351cmdloop(int top)
11352{
11353 union node *n;
11354 struct stackmark smark;
11355 int inter;
11356 int numeof = 0;
11357
11358 TRACE(("cmdloop(%d) called\n", top));
11359 for (;;) {
11360 int skip;
11361
11362 setstackmark(&smark);
11363#if JOBS
11364 if (jobctl)
11365 showjobs(stderr, SHOW_CHANGED);
11366#endif
11367 inter = 0;
11368 if (iflag && top) {
11369 inter++;
11370#if ENABLE_ASH_MAIL
11371 chkmail();
11372#endif
11373 }
11374 n = parsecmd(inter);
11375 /* showtree(n); DEBUG */
11376 if (n == NEOF) {
11377 if (!top || numeof >= 50)
11378 break;
11379 if (!stoppedjobs()) {
11380 if (!Iflag)
11381 break;
11382 out2str("\nUse \"exit\" to leave shell.\n");
11383 }
11384 numeof++;
11385 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011386 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11387 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011388 numeof = 0;
11389 evaltree(n, 0);
11390 }
11391 popstackmark(&smark);
11392 skip = evalskip;
11393
11394 if (skip) {
11395 evalskip = 0;
11396 return skip & SKIPEVAL;
11397 }
11398 }
11399 return 0;
11400}
11401
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011402/*
11403 * Take commands from a file. To be compatible we should do a path
11404 * search for the file, which is necessary to find sub-commands.
11405 */
11406static char *
11407find_dot_file(char *name)
11408{
11409 char *fullname;
11410 const char *path = pathval();
11411 struct stat statb;
11412
11413 /* don't try this for absolute or relative paths */
11414 if (strchr(name, '/'))
11415 return name;
11416
11417 while ((fullname = padvance(&path, name)) != NULL) {
11418 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11419 /*
11420 * Don't bother freeing here, since it will
11421 * be freed by the caller.
11422 */
11423 return fullname;
11424 }
11425 stunalloc(fullname);
11426 }
11427
11428 /* not found in the PATH */
11429 ash_msg_and_raise_error("%s: not found", name);
11430 /* NOTREACHED */
11431}
11432
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011433static int
11434dotcmd(int argc, char **argv)
11435{
11436 struct strlist *sp;
11437 volatile struct shparam saveparam;
11438 int status = 0;
11439
11440 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011441 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011442
Denis Vlasenko68404f12008-03-17 09:00:54 +000011443 if (argv[1]) { /* That's what SVR2 does */
11444 char *fullname = find_dot_file(argv[1]);
11445 argv += 2;
11446 argc -= 2;
11447 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011448 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011449 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011450 shellparam.nparam = argc;
11451 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011452 };
11453
11454 setinputfile(fullname, INPUT_PUSH_FILE);
11455 commandname = fullname;
11456 cmdloop(0);
11457 popfile();
11458
Denis Vlasenko68404f12008-03-17 09:00:54 +000011459 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011460 freeparam(&shellparam);
11461 shellparam = saveparam;
11462 };
11463 status = exitstatus;
11464 }
11465 return status;
11466}
11467
11468static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011469exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011470{
11471 if (stoppedjobs())
11472 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011473 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011474 exitstatus = number(argv[1]);
11475 raise_exception(EXEXIT);
11476 /* NOTREACHED */
11477}
11478
11479#if ENABLE_ASH_BUILTIN_ECHO
11480static int
11481echocmd(int argc, char **argv)
11482{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011483 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011484}
11485#endif
11486
11487#if ENABLE_ASH_BUILTIN_TEST
11488static int
11489testcmd(int argc, char **argv)
11490{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011491 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011492}
11493#endif
11494
11495/*
11496 * Read a file containing shell functions.
11497 */
11498static void
11499readcmdfile(char *name)
11500{
11501 setinputfile(name, INPUT_PUSH_FILE);
11502 cmdloop(0);
11503 popfile();
11504}
11505
11506
Denis Vlasenkocc571512007-02-23 21:10:35 +000011507/* ============ find_command inplementation */
11508
11509/*
11510 * Resolve a command name. If you change this routine, you may have to
11511 * change the shellexec routine as well.
11512 */
11513static void
11514find_command(char *name, struct cmdentry *entry, int act, const char *path)
11515{
11516 struct tblentry *cmdp;
11517 int idx;
11518 int prev;
11519 char *fullname;
11520 struct stat statb;
11521 int e;
11522 int updatetbl;
11523 struct builtincmd *bcmd;
11524
11525 /* If name contains a slash, don't use PATH or hash table */
11526 if (strchr(name, '/') != NULL) {
11527 entry->u.index = -1;
11528 if (act & DO_ABS) {
11529 while (stat(name, &statb) < 0) {
11530#ifdef SYSV
11531 if (errno == EINTR)
11532 continue;
11533#endif
11534 entry->cmdtype = CMDUNKNOWN;
11535 return;
11536 }
11537 }
11538 entry->cmdtype = CMDNORMAL;
11539 return;
11540 }
11541
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011542/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011543
11544 updatetbl = (path == pathval());
11545 if (!updatetbl) {
11546 act |= DO_ALTPATH;
11547 if (strstr(path, "%builtin") != NULL)
11548 act |= DO_ALTBLTIN;
11549 }
11550
11551 /* If name is in the table, check answer will be ok */
11552 cmdp = cmdlookup(name, 0);
11553 if (cmdp != NULL) {
11554 int bit;
11555
11556 switch (cmdp->cmdtype) {
11557 default:
11558#if DEBUG
11559 abort();
11560#endif
11561 case CMDNORMAL:
11562 bit = DO_ALTPATH;
11563 break;
11564 case CMDFUNCTION:
11565 bit = DO_NOFUNC;
11566 break;
11567 case CMDBUILTIN:
11568 bit = DO_ALTBLTIN;
11569 break;
11570 }
11571 if (act & bit) {
11572 updatetbl = 0;
11573 cmdp = NULL;
11574 } else if (cmdp->rehash == 0)
11575 /* if not invalidated by cd, we're done */
11576 goto success;
11577 }
11578
11579 /* If %builtin not in path, check for builtin next */
11580 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011581 if (bcmd) {
11582 if (IS_BUILTIN_REGULAR(bcmd))
11583 goto builtin_success;
11584 if (act & DO_ALTPATH) {
11585 if (!(act & DO_ALTBLTIN))
11586 goto builtin_success;
11587 } else if (builtinloc <= 0) {
11588 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011589 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011590 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011591
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011592#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011593 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011594 entry->cmdtype = CMDNORMAL;
11595 entry->u.index = -1;
11596 return;
11597 }
11598#endif
11599
Denis Vlasenkocc571512007-02-23 21:10:35 +000011600 /* We have to search path. */
11601 prev = -1; /* where to start */
11602 if (cmdp && cmdp->rehash) { /* doing a rehash */
11603 if (cmdp->cmdtype == CMDBUILTIN)
11604 prev = builtinloc;
11605 else
11606 prev = cmdp->param.index;
11607 }
11608
11609 e = ENOENT;
11610 idx = -1;
11611 loop:
11612 while ((fullname = padvance(&path, name)) != NULL) {
11613 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011614 /* NB: code below will still use fullname
11615 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011616 idx++;
11617 if (pathopt) {
11618 if (prefix(pathopt, "builtin")) {
11619 if (bcmd)
11620 goto builtin_success;
11621 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011622 } else if (!(act & DO_NOFUNC)
11623 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011624 /* handled below */
11625 } else {
11626 /* ignore unimplemented options */
11627 continue;
11628 }
11629 }
11630 /* if rehash, don't redo absolute path names */
11631 if (fullname[0] == '/' && idx <= prev) {
11632 if (idx < prev)
11633 continue;
11634 TRACE(("searchexec \"%s\": no change\n", name));
11635 goto success;
11636 }
11637 while (stat(fullname, &statb) < 0) {
11638#ifdef SYSV
11639 if (errno == EINTR)
11640 continue;
11641#endif
11642 if (errno != ENOENT && errno != ENOTDIR)
11643 e = errno;
11644 goto loop;
11645 }
11646 e = EACCES; /* if we fail, this will be the error */
11647 if (!S_ISREG(statb.st_mode))
11648 continue;
11649 if (pathopt) { /* this is a %func directory */
11650 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011651 /* NB: stalloc will return space pointed by fullname
11652 * (because we don't have any intervening allocations
11653 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011654 readcmdfile(fullname);
11655 cmdp = cmdlookup(name, 0);
11656 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11657 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11658 stunalloc(fullname);
11659 goto success;
11660 }
11661 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11662 if (!updatetbl) {
11663 entry->cmdtype = CMDNORMAL;
11664 entry->u.index = idx;
11665 return;
11666 }
11667 INT_OFF;
11668 cmdp = cmdlookup(name, 1);
11669 cmdp->cmdtype = CMDNORMAL;
11670 cmdp->param.index = idx;
11671 INT_ON;
11672 goto success;
11673 }
11674
11675 /* We failed. If there was an entry for this command, delete it */
11676 if (cmdp && updatetbl)
11677 delete_cmd_entry();
11678 if (act & DO_ERR)
11679 ash_msg("%s: %s", name, errmsg(e, "not found"));
11680 entry->cmdtype = CMDUNKNOWN;
11681 return;
11682
11683 builtin_success:
11684 if (!updatetbl) {
11685 entry->cmdtype = CMDBUILTIN;
11686 entry->u.cmd = bcmd;
11687 return;
11688 }
11689 INT_OFF;
11690 cmdp = cmdlookup(name, 1);
11691 cmdp->cmdtype = CMDBUILTIN;
11692 cmdp->param.cmd = bcmd;
11693 INT_ON;
11694 success:
11695 cmdp->rehash = 0;
11696 entry->cmdtype = cmdp->cmdtype;
11697 entry->u = cmdp->param;
11698}
11699
11700
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011701/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011702
Eric Andersencb57d552001-06-28 07:25:16 +000011703/*
Eric Andersencb57d552001-06-28 07:25:16 +000011704 * The trap builtin.
11705 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011706static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011707trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011708{
11709 char *action;
11710 char **ap;
11711 int signo;
11712
Eric Andersenc470f442003-07-28 09:56:35 +000011713 nextopt(nullstr);
11714 ap = argptr;
11715 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011716 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011717 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011718 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011719
Rob Landleyc9c1a412006-07-12 19:17:55 +000011720 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011721 out1fmt("trap -- %s %s\n",
11722 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011723 }
11724 }
11725 return 0;
11726 }
Eric Andersenc470f442003-07-28 09:56:35 +000011727 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011728 action = NULL;
11729 else
11730 action = *ap++;
11731 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011732 signo = get_signum(*ap);
11733 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011734 ash_msg_and_raise_error("%s: bad trap", *ap);
11735 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011736 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011737 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011738 action = NULL;
11739 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011740 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011741 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011742 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011743 trap[signo] = action;
11744 if (signo != 0)
11745 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011746 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011747 ap++;
11748 }
11749 return 0;
11750}
11751
Eric Andersenc470f442003-07-28 09:56:35 +000011752
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011753/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011754
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011755#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011756/*
11757 * Lists available builtins
11758 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011759static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011760helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011761{
11762 int col, i;
11763
11764 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011765 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011766 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011767 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011768 if (col > 60) {
11769 out1fmt("\n");
11770 col = 0;
11771 }
11772 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011773#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011774 {
11775 const char *a = applet_names;
11776 while (*a) {
11777 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11778 if (col > 60) {
11779 out1fmt("\n");
11780 col = 0;
11781 }
11782 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011783 }
11784 }
11785#endif
11786 out1fmt("\n\n");
11787 return EXIT_SUCCESS;
11788}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011789#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011790
Eric Andersencb57d552001-06-28 07:25:16 +000011791/*
Eric Andersencb57d552001-06-28 07:25:16 +000011792 * The export and readonly commands.
11793 */
Eric Andersenc470f442003-07-28 09:56:35 +000011794static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011795exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011796{
11797 struct var *vp;
11798 char *name;
11799 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011800 char **aptr;
11801 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011802
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011803 if (nextopt("p") != 'p') {
11804 aptr = argptr;
11805 name = *aptr;
11806 if (name) {
11807 do {
11808 p = strchr(name, '=');
11809 if (p != NULL) {
11810 p++;
11811 } else {
11812 vp = *findvar(hashvar(name), name);
11813 if (vp) {
11814 vp->flags |= flag;
11815 continue;
11816 }
Eric Andersencb57d552001-06-28 07:25:16 +000011817 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011818 setvar(name, p, flag);
11819 } while ((name = *++aptr) != NULL);
11820 return 0;
11821 }
Eric Andersencb57d552001-06-28 07:25:16 +000011822 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011823 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011824 return 0;
11825}
11826
Eric Andersencb57d552001-06-28 07:25:16 +000011827/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011828 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011829 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011830static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011831unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011832{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011833 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011834
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011835 cmdp = cmdlookup(name, 0);
11836 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11837 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011838}
11839
Eric Andersencb57d552001-06-28 07:25:16 +000011840/*
Eric Andersencb57d552001-06-28 07:25:16 +000011841 * The unset builtin command. We unset the function before we unset the
11842 * variable to allow a function to be unset when there is a readonly variable
11843 * with the same name.
11844 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011845static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011846unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersencb57d552001-06-28 07:25:16 +000011847{
11848 char **ap;
11849 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011850 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011851 int ret = 0;
11852
11853 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011854 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011855 }
Eric Andersencb57d552001-06-28 07:25:16 +000011856
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011857 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011858 if (flag != 'f') {
11859 i = unsetvar(*ap);
11860 ret |= i;
11861 if (!(i & 2))
11862 continue;
11863 }
11864 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011865 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011866 }
Eric Andersenc470f442003-07-28 09:56:35 +000011867 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011868}
11869
11870
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011871/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011872
Eric Andersenc470f442003-07-28 09:56:35 +000011873#include <sys/times.h>
11874
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011875static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011876 ' ', offsetof(struct tms, tms_utime),
11877 '\n', offsetof(struct tms, tms_stime),
11878 ' ', offsetof(struct tms, tms_cutime),
11879 '\n', offsetof(struct tms, tms_cstime),
11880 0
11881};
Eric Andersencb57d552001-06-28 07:25:16 +000011882
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011883static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011884timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011885{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011886 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011887 const unsigned char *p;
11888 struct tms buf;
11889
11890 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011891 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011892
11893 p = timescmd_str;
11894 do {
11895 t = *(clock_t *)(((char *) &buf) + p[1]);
11896 s = t / clk_tck;
11897 out1fmt("%ldm%ld.%.3lds%c",
11898 s/60, s%60,
11899 ((t - s * clk_tck) * 1000) / clk_tck,
11900 p[0]);
11901 } while (*(p += 2));
11902
Eric Andersencb57d552001-06-28 07:25:16 +000011903 return 0;
11904}
11905
Denis Vlasenko131ae172007-02-18 13:00:19 +000011906#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011907static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011908dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011909{
Eric Andersened9ecf72004-06-22 08:29:45 +000011910 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011911 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011912
Denis Vlasenkob012b102007-02-19 22:43:01 +000011913 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011914 result = arith(s, &errcode);
11915 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011916 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011917 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011918 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011919 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011920 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011921 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011922 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011923 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011924 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011925
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011926 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011927}
Eric Andersenc470f442003-07-28 09:56:35 +000011928
Eric Andersenc470f442003-07-28 09:56:35 +000011929/*
Eric Andersen90898442003-08-06 11:20:52 +000011930 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11931 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11932 *
11933 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011934 */
11935static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011936letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011937{
Denis Vlasenko68404f12008-03-17 09:00:54 +000011938 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000011939
Denis Vlasenko68404f12008-03-17 09:00:54 +000011940 argv++;
11941 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011942 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000011943 do {
11944 i = dash_arith(*argv);
11945 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000011946
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011947 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011948}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011949#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011950
Eric Andersenc470f442003-07-28 09:56:35 +000011951
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011952/* ============ miscbltin.c
11953 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011954 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011955 */
11956
11957#undef rflag
11958
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011959#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011960typedef enum __rlimit_resource rlim_t;
11961#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011962
Eric Andersenc470f442003-07-28 09:56:35 +000011963/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011964 * The read builtin. Options:
11965 * -r Do not interpret '\' specially
11966 * -s Turn off echo (tty only)
11967 * -n NCHARS Read NCHARS max
11968 * -p PROMPT Display PROMPT on stderr (if input is from tty)
11969 * -t SECONDS Timeout after SECONDS (tty or pipe only)
11970 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000011971 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011972 * TODO: bash also has:
11973 * -a ARRAY Read into array[0],[1],etc
11974 * -d DELIM End on DELIM char, not newline
11975 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000011976 */
Eric Andersenc470f442003-07-28 09:56:35 +000011977static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000011978readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000011979{
11980 char **ap;
11981 int backslash;
11982 char c;
11983 int rflag;
11984 char *prompt;
11985 const char *ifs;
11986 char *p;
11987 int startword;
11988 int status;
11989 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011990 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011991#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011992 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000011993 int silent = 0;
11994 struct termios tty, old_tty;
11995#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011996#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000011997 unsigned end_ms = 0;
11998 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011999#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012000
12001 rflag = 0;
12002 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012003 while ((i = nextopt("p:u:r"
12004 USE_ASH_READ_TIMEOUT("t:")
12005 USE_ASH_READ_NCHARS("n:s")
12006 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012007 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012008 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012009 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012010 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012011#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012012 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012013 nchars = bb_strtou(optionarg, NULL, 10);
12014 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012015 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012016 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012017 break;
12018 case 's':
12019 silent = 1;
12020 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012021#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012022#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012023 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012024 timeout = bb_strtou(optionarg, NULL, 10);
12025 if (errno || timeout > UINT_MAX / 2048)
12026 ash_msg_and_raise_error("invalid timeout");
12027 timeout *= 1000;
12028#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012029 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012030 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012031 /* EINVAL means number is ok, but not terminated by NUL */
12032 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012033 char *p2;
12034 if (*++p) {
12035 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012036 ts.tv_usec = bb_strtou(p, &p2, 10);
12037 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012038 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012039 scale = p2 - p;
12040 /* normalize to usec */
12041 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012042 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012043 while (scale++ < 6)
12044 ts.tv_usec *= 10;
12045 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012046 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012047 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012048 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012049 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012050 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012051 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012052#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012053 break;
12054#endif
12055 case 'r':
12056 rflag = 1;
12057 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012058 case 'u':
12059 fd = bb_strtou(optionarg, NULL, 10);
12060 if (fd < 0 || errno)
12061 ash_msg_and_raise_error("invalid file descriptor");
12062 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012063 default:
12064 break;
12065 }
Eric Andersenc470f442003-07-28 09:56:35 +000012066 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012067 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012068 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012069 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012070 ap = argptr;
12071 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012072 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012073 ifs = bltinlookup("IFS");
12074 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012075 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012076#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012077 tcgetattr(fd, &tty);
12078 old_tty = tty;
12079 if (nchars || silent) {
12080 if (nchars) {
12081 tty.c_lflag &= ~ICANON;
12082 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012083 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012084 if (silent) {
12085 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12086 }
12087 /* if tcgetattr failed, tcsetattr will fail too.
12088 * Ignoring, it's harmless. */
12089 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012090 }
12091#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012092
Eric Andersenc470f442003-07-28 09:56:35 +000012093 status = 0;
12094 startword = 1;
12095 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012096#if ENABLE_ASH_READ_TIMEOUT
12097 if (timeout) /* NB: ensuring end_ms is nonzero */
12098 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12099#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012100 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012101 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012102#if ENABLE_ASH_READ_TIMEOUT
12103 if (end_ms) {
12104 struct pollfd pfd[1];
12105 pfd[0].fd = fd;
12106 pfd[0].events = POLLIN;
12107 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12108 if ((int)timeout <= 0 /* already late? */
12109 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12110 ) { /* timed out! */
12111#if ENABLE_ASH_READ_NCHARS
12112 tcsetattr(fd, TCSANOW, &old_tty);
12113#endif
12114 return 1;
12115 }
12116 }
12117#endif
12118 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012119 status = 1;
12120 break;
12121 }
12122 if (c == '\0')
12123 continue;
12124 if (backslash) {
12125 backslash = 0;
12126 if (c != '\n')
12127 goto put;
12128 continue;
12129 }
12130 if (!rflag && c == '\\') {
12131 backslash++;
12132 continue;
12133 }
12134 if (c == '\n')
12135 break;
12136 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12137 continue;
12138 }
12139 startword = 0;
12140 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12141 STACKSTRNUL(p);
12142 setvar(*ap, stackblock(), 0);
12143 ap++;
12144 startword = 1;
12145 STARTSTACKSTR(p);
12146 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012147 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012148 STPUTC(c, p);
12149 }
12150 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012151/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012152#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012153 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012154#else
12155 while (1);
12156#endif
12157
12158#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012159 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012160#endif
12161
Eric Andersenc470f442003-07-28 09:56:35 +000012162 STACKSTRNUL(p);
12163 /* Remove trailing blanks */
12164 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12165 *p = '\0';
12166 setvar(*ap, stackblock(), 0);
12167 while (*++ap != NULL)
12168 setvar(*ap, nullstr, 0);
12169 return status;
12170}
12171
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012172static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012173umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012174{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012175 static const char permuser[3] ALIGN1 = "ugo";
12176 static const char permmode[3] ALIGN1 = "rwx";
12177 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012178 S_IRUSR, S_IWUSR, S_IXUSR,
12179 S_IRGRP, S_IWGRP, S_IXGRP,
12180 S_IROTH, S_IWOTH, S_IXOTH
12181 };
12182
12183 char *ap;
12184 mode_t mask;
12185 int i;
12186 int symbolic_mode = 0;
12187
12188 while (nextopt("S") != '\0') {
12189 symbolic_mode = 1;
12190 }
12191
Denis Vlasenkob012b102007-02-19 22:43:01 +000012192 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012193 mask = umask(0);
12194 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012195 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012196
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012197 ap = *argptr;
12198 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012199 if (symbolic_mode) {
12200 char buf[18];
12201 char *p = buf;
12202
12203 for (i = 0; i < 3; i++) {
12204 int j;
12205
12206 *p++ = permuser[i];
12207 *p++ = '=';
12208 for (j = 0; j < 3; j++) {
12209 if ((mask & permmask[3 * i + j]) == 0) {
12210 *p++ = permmode[j];
12211 }
12212 }
12213 *p++ = ',';
12214 }
12215 *--p = 0;
12216 puts(buf);
12217 } else {
12218 out1fmt("%.4o\n", mask);
12219 }
12220 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012221 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012222 mask = 0;
12223 do {
12224 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012225 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012226 mask = (mask << 3) + (*ap - '0');
12227 } while (*++ap != '\0');
12228 umask(mask);
12229 } else {
12230 mask = ~mask & 0777;
12231 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012232 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012233 }
12234 umask(~mask & 0777);
12235 }
12236 }
12237 return 0;
12238}
12239
12240/*
12241 * ulimit builtin
12242 *
12243 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12244 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12245 * ash by J.T. Conklin.
12246 *
12247 * Public domain.
12248 */
12249
12250struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012251 uint8_t cmd; /* RLIMIT_xxx fit into it */
12252 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012253 char option;
12254};
12255
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012256static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012257#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012258 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012259#endif
12260#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012261 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012262#endif
12263#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012264 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012265#endif
12266#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012267 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012268#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012269#ifdef RLIMIT_CORE
12270 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012271#endif
12272#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012273 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012274#endif
12275#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012276 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012277#endif
12278#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012279 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012280#endif
12281#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012282 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012283#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012284#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012285 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012286#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012287#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012288 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012289#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012290};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012291static const char limits_name[] =
12292#ifdef RLIMIT_CPU
12293 "time(seconds)" "\0"
12294#endif
12295#ifdef RLIMIT_FSIZE
12296 "file(blocks)" "\0"
12297#endif
12298#ifdef RLIMIT_DATA
12299 "data(kb)" "\0"
12300#endif
12301#ifdef RLIMIT_STACK
12302 "stack(kb)" "\0"
12303#endif
12304#ifdef RLIMIT_CORE
12305 "coredump(blocks)" "\0"
12306#endif
12307#ifdef RLIMIT_RSS
12308 "memory(kb)" "\0"
12309#endif
12310#ifdef RLIMIT_MEMLOCK
12311 "locked memory(kb)" "\0"
12312#endif
12313#ifdef RLIMIT_NPROC
12314 "process" "\0"
12315#endif
12316#ifdef RLIMIT_NOFILE
12317 "nofiles" "\0"
12318#endif
12319#ifdef RLIMIT_AS
12320 "vmemory(kb)" "\0"
12321#endif
12322#ifdef RLIMIT_LOCKS
12323 "locks" "\0"
12324#endif
12325;
Eric Andersenc470f442003-07-28 09:56:35 +000012326
Glenn L McGrath76620622004-01-13 10:19:37 +000012327enum limtype { SOFT = 0x1, HARD = 0x2 };
12328
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012329static void
12330printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012331 const struct limits *l)
12332{
12333 rlim_t val;
12334
12335 val = limit->rlim_max;
12336 if (how & SOFT)
12337 val = limit->rlim_cur;
12338
12339 if (val == RLIM_INFINITY)
12340 out1fmt("unlimited\n");
12341 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012342 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012343 out1fmt("%lld\n", (long long) val);
12344 }
12345}
12346
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012347static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000012348ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
Eric Andersenc470f442003-07-28 09:56:35 +000012349{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012350 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012351 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012352 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012353 const struct limits *l;
12354 int set, all = 0;
12355 int optc, what;
12356 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012357
12358 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012359 while ((optc = nextopt("HSa"
12360#ifdef RLIMIT_CPU
12361 "t"
12362#endif
12363#ifdef RLIMIT_FSIZE
12364 "f"
12365#endif
12366#ifdef RLIMIT_DATA
12367 "d"
12368#endif
12369#ifdef RLIMIT_STACK
12370 "s"
12371#endif
12372#ifdef RLIMIT_CORE
12373 "c"
12374#endif
12375#ifdef RLIMIT_RSS
12376 "m"
12377#endif
12378#ifdef RLIMIT_MEMLOCK
12379 "l"
12380#endif
12381#ifdef RLIMIT_NPROC
12382 "p"
12383#endif
12384#ifdef RLIMIT_NOFILE
12385 "n"
12386#endif
12387#ifdef RLIMIT_AS
12388 "v"
12389#endif
12390#ifdef RLIMIT_LOCKS
12391 "w"
12392#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012393 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012394 switch (optc) {
12395 case 'H':
12396 how = HARD;
12397 break;
12398 case 'S':
12399 how = SOFT;
12400 break;
12401 case 'a':
12402 all = 1;
12403 break;
12404 default:
12405 what = optc;
12406 }
12407
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012408 for (l = limits_tbl; l->option != what; l++)
12409 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012410
12411 set = *argptr ? 1 : 0;
12412 if (set) {
12413 char *p = *argptr;
12414
12415 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012416 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012417 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012418 val = RLIM_INFINITY;
12419 else {
12420 val = (rlim_t) 0;
12421
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012422 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012423 val = (val * 10) + (long)(c - '0');
12424 if (val < (rlim_t) 0)
12425 break;
12426 }
12427 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012428 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012429 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012430 }
12431 }
12432 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012433 const char *lname = limits_name;
12434 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012435 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012436 out1fmt("%-20s ", lname);
12437 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012438 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012439 }
12440 return 0;
12441 }
12442
12443 getrlimit(l->cmd, &limit);
12444 if (set) {
12445 if (how & HARD)
12446 limit.rlim_max = val;
12447 if (how & SOFT)
12448 limit.rlim_cur = val;
12449 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012450 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012451 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012452 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012453 }
12454 return 0;
12455}
12456
Eric Andersen90898442003-08-06 11:20:52 +000012457
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012458/* ============ Math support */
12459
Denis Vlasenko131ae172007-02-18 13:00:19 +000012460#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012461
12462/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12463
12464 Permission is hereby granted, free of charge, to any person obtaining
12465 a copy of this software and associated documentation files (the
12466 "Software"), to deal in the Software without restriction, including
12467 without limitation the rights to use, copy, modify, merge, publish,
12468 distribute, sublicense, and/or sell copies of the Software, and to
12469 permit persons to whom the Software is furnished to do so, subject to
12470 the following conditions:
12471
12472 The above copyright notice and this permission notice shall be
12473 included in all copies or substantial portions of the Software.
12474
12475 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12476 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12477 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12478 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12479 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12480 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12481 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12482*/
12483
12484/* This is my infix parser/evaluator. It is optimized for size, intended
12485 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012486 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012487 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012488 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012489 * be that which POSIX specifies for shells. */
12490
12491/* The code uses a simple two-stack algorithm. See
12492 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012493 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012494 * this is based (this code differs in that it applies operators immediately
12495 * to the stack instead of adding them to a queue to end up with an
12496 * expression). */
12497
12498/* To use the routine, call it with an expression string and error return
12499 * pointer */
12500
12501/*
12502 * Aug 24, 2001 Manuel Novoa III
12503 *
12504 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12505 *
12506 * 1) In arith_apply():
12507 * a) Cached values of *numptr and &(numptr[-1]).
12508 * b) Removed redundant test for zero denominator.
12509 *
12510 * 2) In arith():
12511 * a) Eliminated redundant code for processing operator tokens by moving
12512 * to a table-based implementation. Also folded handling of parens
12513 * into the table.
12514 * b) Combined all 3 loops which called arith_apply to reduce generated
12515 * code size at the cost of speed.
12516 *
12517 * 3) The following expressions were treated as valid by the original code:
12518 * 1() , 0! , 1 ( *3 ) .
12519 * These bugs have been fixed by internally enclosing the expression in
12520 * parens and then checking that all binary ops and right parens are
12521 * preceded by a valid expression (NUM_TOKEN).
12522 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012523 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012524 * ctype's isspace() if it is used by another busybox applet or if additional
12525 * whitespace chars should be considered. Look below the "#include"s for a
12526 * precompiler test.
12527 */
12528
12529/*
12530 * Aug 26, 2001 Manuel Novoa III
12531 *
12532 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12533 *
12534 * Merge in Aaron's comments previously posted to the busybox list,
12535 * modified slightly to take account of my changes to the code.
12536 *
12537 */
12538
12539/*
12540 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12541 *
12542 * - allow access to variable,
12543 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12544 * - realize assign syntax (VAR=expr, +=, *= etc)
12545 * - realize exponentiation (** operator)
12546 * - realize comma separated - expr, expr
12547 * - realise ++expr --expr expr++ expr--
12548 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012549 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012550 * - was restored loses XOR operator
12551 * - remove one goto label, added three ;-)
12552 * - protect $((num num)) as true zero expr (Manuel`s error)
12553 * - always use special isspace(), see comment from bash ;-)
12554 */
12555
Eric Andersen90898442003-08-06 11:20:52 +000012556#define arith_isspace(arithval) \
12557 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12558
Eric Andersen90898442003-08-06 11:20:52 +000012559typedef unsigned char operator;
12560
12561/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012562 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012563 * precedence. The ID portion is so that multiple operators can have the
12564 * same precedence, ensuring that the leftmost one is evaluated first.
12565 * Consider * and /. */
12566
12567#define tok_decl(prec,id) (((id)<<5)|(prec))
12568#define PREC(op) ((op) & 0x1F)
12569
12570#define TOK_LPAREN tok_decl(0,0)
12571
12572#define TOK_COMMA tok_decl(1,0)
12573
12574#define TOK_ASSIGN tok_decl(2,0)
12575#define TOK_AND_ASSIGN tok_decl(2,1)
12576#define TOK_OR_ASSIGN tok_decl(2,2)
12577#define TOK_XOR_ASSIGN tok_decl(2,3)
12578#define TOK_PLUS_ASSIGN tok_decl(2,4)
12579#define TOK_MINUS_ASSIGN tok_decl(2,5)
12580#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12581#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12582
12583#define TOK_MUL_ASSIGN tok_decl(3,0)
12584#define TOK_DIV_ASSIGN tok_decl(3,1)
12585#define TOK_REM_ASSIGN tok_decl(3,2)
12586
12587/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012588#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012589
12590/* conditional is right associativity too */
12591#define TOK_CONDITIONAL tok_decl(4,0)
12592#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12593
12594#define TOK_OR tok_decl(5,0)
12595
12596#define TOK_AND tok_decl(6,0)
12597
12598#define TOK_BOR tok_decl(7,0)
12599
12600#define TOK_BXOR tok_decl(8,0)
12601
12602#define TOK_BAND tok_decl(9,0)
12603
12604#define TOK_EQ tok_decl(10,0)
12605#define TOK_NE tok_decl(10,1)
12606
12607#define TOK_LT tok_decl(11,0)
12608#define TOK_GT tok_decl(11,1)
12609#define TOK_GE tok_decl(11,2)
12610#define TOK_LE tok_decl(11,3)
12611
12612#define TOK_LSHIFT tok_decl(12,0)
12613#define TOK_RSHIFT tok_decl(12,1)
12614
12615#define TOK_ADD tok_decl(13,0)
12616#define TOK_SUB tok_decl(13,1)
12617
12618#define TOK_MUL tok_decl(14,0)
12619#define TOK_DIV tok_decl(14,1)
12620#define TOK_REM tok_decl(14,2)
12621
12622/* exponent is right associativity */
12623#define TOK_EXPONENT tok_decl(15,1)
12624
12625/* For now unary operators. */
12626#define UNARYPREC 16
12627#define TOK_BNOT tok_decl(UNARYPREC,0)
12628#define TOK_NOT tok_decl(UNARYPREC,1)
12629
12630#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12631#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12632
12633#define PREC_PRE (UNARYPREC+2)
12634
12635#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12636#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12637
12638#define PREC_POST (UNARYPREC+3)
12639
12640#define TOK_POST_INC tok_decl(PREC_POST, 0)
12641#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12642
12643#define SPEC_PREC (UNARYPREC+4)
12644
12645#define TOK_NUM tok_decl(SPEC_PREC, 0)
12646#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12647
12648#define NUMPTR (*numstackptr)
12649
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012650static int
12651tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012652{
12653 operator prec = PREC(op);
12654
12655 convert_prec_is_assing(prec);
12656 return (prec == PREC(TOK_ASSIGN) ||
12657 prec == PREC_PRE || prec == PREC_POST);
12658}
12659
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012660static int
12661is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012662{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012663 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12664 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012665}
12666
Eric Andersen90898442003-08-06 11:20:52 +000012667typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012668 arith_t val;
12669 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012670 char contidional_second_val_initialized;
12671 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012672 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012673} v_n_t;
12674
Eric Andersen90898442003-08-06 11:20:52 +000012675typedef struct CHK_VAR_RECURSIVE_LOOPED {
12676 const char *var;
12677 struct CHK_VAR_RECURSIVE_LOOPED *next;
12678} chk_var_recursive_looped_t;
12679
12680static chk_var_recursive_looped_t *prev_chk_var_recursive;
12681
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012682static int
12683arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012684{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012685 if (t->var) {
12686 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012687
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012688 if (p) {
12689 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012690
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012691 /* recursive try as expression */
12692 chk_var_recursive_looped_t *cur;
12693 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012694
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012695 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12696 if (strcmp(cur->var, t->var) == 0) {
12697 /* expression recursion loop detected */
12698 return -5;
12699 }
12700 }
12701 /* save current lookuped var name */
12702 cur = prev_chk_var_recursive;
12703 cur_save.var = t->var;
12704 cur_save.next = cur;
12705 prev_chk_var_recursive = &cur_save;
12706
12707 t->val = arith (p, &errcode);
12708 /* restore previous ptr after recursiving */
12709 prev_chk_var_recursive = cur;
12710 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012711 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012712 /* allow undefined var as 0 */
12713 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012714 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012715 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012716}
12717
12718/* "applying" a token means performing it on the top elements on the integer
12719 * stack. For a unary operator it will only change the top element, but a
12720 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012721static int
12722arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012723{
Eric Andersen90898442003-08-06 11:20:52 +000012724 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012725 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012726 int ret_arith_lookup_val;
12727
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012728 /* There is no operator that can work without arguments */
12729 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012730 numptr_m1 = NUMPTR - 1;
12731
12732 /* check operand is var with noninteger value */
12733 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012734 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012735 return ret_arith_lookup_val;
12736
12737 rez = numptr_m1->val;
12738 if (op == TOK_UMINUS)
12739 rez *= -1;
12740 else if (op == TOK_NOT)
12741 rez = !rez;
12742 else if (op == TOK_BNOT)
12743 rez = ~rez;
12744 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12745 rez++;
12746 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12747 rez--;
12748 else if (op != TOK_UPLUS) {
12749 /* Binary operators */
12750
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012751 /* check and binary operators need two arguments */
12752 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012753
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012754 /* ... and they pop one */
12755 --NUMPTR;
12756 numptr_val = rez;
12757 if (op == TOK_CONDITIONAL) {
12758 if (! numptr_m1->contidional_second_val_initialized) {
12759 /* protect $((expr1 ? expr2)) without ": expr" */
12760 goto err;
12761 }
12762 rez = numptr_m1->contidional_second_val;
12763 } else if (numptr_m1->contidional_second_val_initialized) {
12764 /* protect $((expr1 : expr2)) without "expr ? " */
12765 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012766 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012767 numptr_m1 = NUMPTR - 1;
12768 if (op != TOK_ASSIGN) {
12769 /* check operand is var with noninteger value for not '=' */
12770 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12771 if (ret_arith_lookup_val)
12772 return ret_arith_lookup_val;
12773 }
12774 if (op == TOK_CONDITIONAL) {
12775 numptr_m1->contidional_second_val = rez;
12776 }
12777 rez = numptr_m1->val;
12778 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012779 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012780 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012781 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012782 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012783 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012784 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012785 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012786 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012787 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012788 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012789 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012790 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012791 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012792 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012793 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012794 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012795 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012796 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012797 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012798 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012799 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012800 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012801 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012802 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012803 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012804 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012805 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012806 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012807 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012808 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012809 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012810 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012811 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012812 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012813 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012814 /* protect $((expr : expr)) without "expr ? " */
12815 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012816 }
12817 numptr_m1->contidional_second_val_initialized = op;
12818 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012819 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012820 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012821 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012822 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012823 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012824 return -3; /* exponent less than 0 */
12825 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012826 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012827
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012828 if (numptr_val)
12829 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012830 c *= rez;
12831 rez = c;
12832 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012833 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012834 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012835 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012836 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012837 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012838 rez %= numptr_val;
12839 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012840 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012841 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012842
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012843 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012844 /* Hmm, 1=2 ? */
12845 goto err;
12846 }
12847 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012848#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012849 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012850#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012851 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012852#endif
Eric Andersen90898442003-08-06 11:20:52 +000012853 setvar(numptr_m1->var, buf, 0);
12854 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012855 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012856 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012857 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012858 rez++;
12859 }
12860 numptr_m1->val = rez;
12861 /* protect geting var value, is number now */
12862 numptr_m1->var = NULL;
12863 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012864 err:
12865 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012866}
12867
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012868/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012869static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012870 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12871 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12872 '<','<', 0, TOK_LSHIFT,
12873 '>','>', 0, TOK_RSHIFT,
12874 '|','|', 0, TOK_OR,
12875 '&','&', 0, TOK_AND,
12876 '!','=', 0, TOK_NE,
12877 '<','=', 0, TOK_LE,
12878 '>','=', 0, TOK_GE,
12879 '=','=', 0, TOK_EQ,
12880 '|','=', 0, TOK_OR_ASSIGN,
12881 '&','=', 0, TOK_AND_ASSIGN,
12882 '*','=', 0, TOK_MUL_ASSIGN,
12883 '/','=', 0, TOK_DIV_ASSIGN,
12884 '%','=', 0, TOK_REM_ASSIGN,
12885 '+','=', 0, TOK_PLUS_ASSIGN,
12886 '-','=', 0, TOK_MINUS_ASSIGN,
12887 '-','-', 0, TOK_POST_DEC,
12888 '^','=', 0, TOK_XOR_ASSIGN,
12889 '+','+', 0, TOK_POST_INC,
12890 '*','*', 0, TOK_EXPONENT,
12891 '!', 0, TOK_NOT,
12892 '<', 0, TOK_LT,
12893 '>', 0, TOK_GT,
12894 '=', 0, TOK_ASSIGN,
12895 '|', 0, TOK_BOR,
12896 '&', 0, TOK_BAND,
12897 '*', 0, TOK_MUL,
12898 '/', 0, TOK_DIV,
12899 '%', 0, TOK_REM,
12900 '+', 0, TOK_ADD,
12901 '-', 0, TOK_SUB,
12902 '^', 0, TOK_BXOR,
12903 /* uniq */
12904 '~', 0, TOK_BNOT,
12905 ',', 0, TOK_COMMA,
12906 '?', 0, TOK_CONDITIONAL,
12907 ':', 0, TOK_CONDITIONAL_SEP,
12908 ')', 0, TOK_RPAREN,
12909 '(', 0, TOK_LPAREN,
12910 0
12911};
12912/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012913#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000012914
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012915static arith_t
12916arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012917{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012918 char arithval; /* Current character under analysis */
12919 operator lasttok, op;
12920 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012921
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012922 const char *p = endexpression;
12923 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012924
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012925 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012926
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012927 /* Stack of integers */
12928 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12929 * in any given correct or incorrect expression is left as an exercise to
12930 * the reader. */
12931 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12932 *numstackptr = numstack;
12933 /* Stack of operator tokens */
12934 operator *stack = alloca((datasizes) * sizeof(operator)),
12935 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012936
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012937 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12938 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012939
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012940 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012941 arithval = *expr;
12942 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012943 if (p == endexpression) {
12944 /* Null expression. */
12945 return 0;
12946 }
12947
12948 /* This is only reached after all tokens have been extracted from the
12949 * input stream. If there are still tokens on the operator stack, they
12950 * are to be applied in order. At the end, there should be a final
12951 * result on the integer stack */
12952
12953 if (expr != endexpression + 1) {
12954 /* If we haven't done so already, */
12955 /* append a closing right paren */
12956 expr = endexpression;
12957 /* and let the loop process it. */
12958 continue;
12959 }
12960 /* At this point, we're done with the expression. */
12961 if (numstackptr != numstack+1) {
12962 /* ... but if there isn't, it's bad */
12963 err:
12964 return (*perrcode = -1);
12965 }
12966 if (numstack->var) {
12967 /* expression is $((var)) only, lookup now */
12968 errcode = arith_lookup_val(numstack);
12969 }
12970 ret:
12971 *perrcode = errcode;
12972 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012973 }
12974
Eric Andersen90898442003-08-06 11:20:52 +000012975 /* Continue processing the expression. */
12976 if (arith_isspace(arithval)) {
12977 /* Skip whitespace */
12978 goto prologue;
12979 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012980 p = endofname(expr);
12981 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012982 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012983
12984 numstackptr->var = alloca(var_name_size);
12985 safe_strncpy(numstackptr->var, expr, var_name_size);
12986 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012987 num:
Eric Andersen90898442003-08-06 11:20:52 +000012988 numstackptr->contidional_second_val_initialized = 0;
12989 numstackptr++;
12990 lasttok = TOK_NUM;
12991 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012992 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012993 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012994 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012995#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012996 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012997#else
12998 numstackptr->val = strtol(expr, (char **) &expr, 0);
12999#endif
Eric Andersen90898442003-08-06 11:20:52 +000013000 goto num;
13001 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013002 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013003 const char *o;
13004
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013005 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013006 /* strange operator not found */
13007 goto err;
13008 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013009 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013010 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013011 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000013012 /* found */
13013 expr = o - 1;
13014 break;
13015 }
13016 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013017 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013018 p++;
13019 /* skip zero delim */
13020 p++;
13021 }
13022 op = p[1];
13023
13024 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013025 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13026 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013027
13028 /* Plus and minus are binary (not unary) _only_ if the last
13029 * token was as number, or a right paren (which pretends to be
13030 * a number, since it evaluates to one). Think about it.
13031 * It makes sense. */
13032 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013033 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013034 case TOK_ADD:
13035 op = TOK_UPLUS;
13036 break;
13037 case TOK_SUB:
13038 op = TOK_UMINUS;
13039 break;
13040 case TOK_POST_INC:
13041 op = TOK_PRE_INC;
13042 break;
13043 case TOK_POST_DEC:
13044 op = TOK_PRE_DEC;
13045 break;
Eric Andersen90898442003-08-06 11:20:52 +000013046 }
13047 }
13048 /* We don't want a unary operator to cause recursive descent on the
13049 * stack, because there can be many in a row and it could cause an
13050 * operator to be evaluated before its argument is pushed onto the
13051 * integer stack. */
13052 /* But for binary operators, "apply" everything on the operator
13053 * stack until we find an operator with a lesser priority than the
13054 * one we have just extracted. */
13055 /* Left paren is given the lowest priority so it will never be
13056 * "applied" in this way.
13057 * if associativity is right and priority eq, applied also skip
13058 */
13059 prec = PREC(op);
13060 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13061 /* not left paren or unary */
13062 if (lasttok != TOK_NUM) {
13063 /* binary op must be preceded by a num */
13064 goto err;
13065 }
13066 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013067 if (op == TOK_RPAREN) {
13068 /* The algorithm employed here is simple: while we don't
13069 * hit an open paren nor the bottom of the stack, pop
13070 * tokens and apply them */
13071 if (stackptr[-1] == TOK_LPAREN) {
13072 --stackptr;
13073 /* Any operator directly after a */
13074 lasttok = TOK_NUM;
13075 /* close paren should consider itself binary */
13076 goto prologue;
13077 }
13078 } else {
13079 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013080
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013081 convert_prec_is_assing(prec);
13082 convert_prec_is_assing(prev_prec);
13083 if (prev_prec < prec)
13084 break;
13085 /* check right assoc */
13086 if (prev_prec == prec && is_right_associativity(prec))
13087 break;
13088 }
13089 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13090 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013091 }
13092 if (op == TOK_RPAREN) {
13093 goto err;
13094 }
13095 }
13096
13097 /* Push this operator to the stack and remember it. */
13098 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013099 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013100 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013101 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013102}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013103#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013104
13105
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013106/* ============ main() and helpers */
13107
13108/*
13109 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013110 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013111static void exitshell(void) ATTRIBUTE_NORETURN;
13112static void
13113exitshell(void)
13114{
13115 struct jmploc loc;
13116 char *p;
13117 int status;
13118
13119 status = exitstatus;
13120 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13121 if (setjmp(loc.loc)) {
13122 if (exception == EXEXIT)
13123/* dash bug: it just does _exit(exitstatus) here
13124 * but we have to do setjobctl(0) first!
13125 * (bug is still not fixed in dash-0.5.3 - if you run dash
13126 * under Midnight Commander, on exit from dash MC is backgrounded) */
13127 status = exitstatus;
13128 goto out;
13129 }
13130 exception_handler = &loc;
13131 p = trap[0];
13132 if (p) {
13133 trap[0] = NULL;
13134 evalstring(p, 0);
13135 }
13136 flush_stdout_stderr();
13137 out:
13138 setjobctl(0);
13139 _exit(status);
13140 /* NOTREACHED */
13141}
13142
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013143static void
13144init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013145{
13146 /* from input.c: */
13147 basepf.nextc = basepf.buf = basebuf;
13148
13149 /* from trap.c: */
13150 signal(SIGCHLD, SIG_DFL);
13151
13152 /* from var.c: */
13153 {
13154 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013155 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013156 const char *p;
13157 struct stat st1, st2;
13158
13159 initvar();
13160 for (envp = environ; envp && *envp; envp++) {
13161 if (strchr(*envp, '=')) {
13162 setvareq(*envp, VEXPORT|VTEXTFIXED);
13163 }
13164 }
13165
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013166 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013167 setvar("PPID", ppid, 0);
13168
13169 p = lookupvar("PWD");
13170 if (p)
13171 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13172 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13173 p = '\0';
13174 setpwd(p, 0);
13175 }
13176}
13177
13178/*
13179 * Process the shell command line arguments.
13180 */
13181static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013182procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013183{
13184 int i;
13185 const char *xminusc;
13186 char **xargv;
13187
13188 xargv = argv;
13189 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013190 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013191 xargv++;
13192 for (i = 0; i < NOPTS; i++)
13193 optlist[i] = 2;
13194 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013195 if (options(1)) {
13196 /* it already printed err message */
13197 raise_exception(EXERROR);
13198 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013199 xargv = argptr;
13200 xminusc = minusc;
13201 if (*xargv == NULL) {
13202 if (xminusc)
13203 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13204 sflag = 1;
13205 }
13206 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13207 iflag = 1;
13208 if (mflag == 2)
13209 mflag = iflag;
13210 for (i = 0; i < NOPTS; i++)
13211 if (optlist[i] == 2)
13212 optlist[i] = 0;
13213#if DEBUG == 2
13214 debug = 1;
13215#endif
13216 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13217 if (xminusc) {
13218 minusc = *xargv++;
13219 if (*xargv)
13220 goto setarg0;
13221 } else if (!sflag) {
13222 setinputfile(*xargv, 0);
13223 setarg0:
13224 arg0 = *xargv++;
13225 commandname = arg0;
13226 }
13227
13228 shellparam.p = xargv;
13229#if ENABLE_ASH_GETOPTS
13230 shellparam.optind = 1;
13231 shellparam.optoff = -1;
13232#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013233 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013234 while (*xargv) {
13235 shellparam.nparam++;
13236 xargv++;
13237 }
13238 optschanged();
13239}
13240
13241/*
13242 * Read /etc/profile or .profile.
13243 */
13244static void
13245read_profile(const char *name)
13246{
13247 int skip;
13248
13249 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13250 return;
13251 skip = cmdloop(0);
13252 popfile();
13253 if (skip)
13254 exitshell();
13255}
13256
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013257/*
13258 * This routine is called when an error or an interrupt occurs in an
13259 * interactive shell and control is returned to the main command loop.
13260 */
13261static void
13262reset(void)
13263{
13264 /* from eval.c: */
13265 evalskip = 0;
13266 loopnest = 0;
13267 /* from input.c: */
13268 parselleft = parsenleft = 0; /* clear input buffer */
13269 popallfiles();
13270 /* from parser.c: */
13271 tokpushback = 0;
13272 checkkwd = 0;
13273 /* from redir.c: */
13274 clearredir(0);
13275}
13276
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013277#if PROFILE
13278static short profile_buf[16384];
13279extern int etext();
13280#endif
13281
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013282/*
13283 * Main routine. We initialize things, parse the arguments, execute
13284 * profiles if we're a login shell, and then call cmdloop to execute
13285 * commands. The setjmp call sets up the location to jump to when an
13286 * exception occurs. When an exception occurs the variable "state"
13287 * is used to figure out how far we had gotten.
13288 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013289int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +000013290int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013291{
13292 char *shinit;
13293 volatile int state;
13294 struct jmploc jmploc;
13295 struct stackmark smark;
13296
Denis Vlasenko01631112007-12-16 17:20:38 +000013297 /* Initialize global data */
13298 INIT_G_misc();
13299 INIT_G_memstack();
13300 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013301#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013302 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013303#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013304 INIT_G_cmdtable();
13305
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013306#if PROFILE
13307 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13308#endif
13309
13310#if ENABLE_FEATURE_EDITING
13311 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13312#endif
13313 state = 0;
13314 if (setjmp(jmploc.loc)) {
13315 int e;
13316 int s;
13317
13318 reset();
13319
13320 e = exception;
13321 if (e == EXERROR)
13322 exitstatus = 2;
13323 s = state;
13324 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13325 exitshell();
13326
13327 if (e == EXINT) {
13328 outcslow('\n', stderr);
13329 }
13330 popstackmark(&smark);
13331 FORCE_INT_ON; /* enable interrupts */
13332 if (s == 1)
13333 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013334 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013335 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013336 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013337 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013338 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013339 }
13340 exception_handler = &jmploc;
13341#if DEBUG
13342 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013343 trace_puts("Shell args: ");
13344 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013345#endif
13346 rootpid = getpid();
13347
13348#if ENABLE_ASH_RANDOM_SUPPORT
13349 rseed = rootpid + time(NULL);
13350#endif
13351 init();
13352 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013353 procargs(argv);
13354
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013355#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13356 if (iflag) {
13357 const char *hp = lookupvar("HISTFILE");
13358
13359 if (hp == NULL) {
13360 hp = lookupvar("HOME");
13361 if (hp != NULL) {
13362 char *defhp = concat_path_file(hp, ".ash_history");
13363 setvar("HISTFILE", defhp, 0);
13364 free(defhp);
13365 }
13366 }
13367 }
13368#endif
13369 if (argv[0] && argv[0][0] == '-')
13370 isloginsh = 1;
13371 if (isloginsh) {
13372 state = 1;
13373 read_profile("/etc/profile");
13374 state1:
13375 state = 2;
13376 read_profile(".profile");
13377 }
13378 state2:
13379 state = 3;
13380 if (
13381#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013382 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013383#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013384 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013385 ) {
13386 shinit = lookupvar("ENV");
13387 if (shinit != NULL && *shinit != '\0') {
13388 read_profile(shinit);
13389 }
13390 }
13391 state3:
13392 state = 4;
13393 if (minusc)
13394 evalstring(minusc, 0);
13395
13396 if (sflag || minusc == NULL) {
13397#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13398 if ( iflag ) {
13399 const char *hp = lookupvar("HISTFILE");
13400
13401 if (hp != NULL)
13402 line_input_state->hist_file = hp;
13403 }
13404#endif
13405 state4: /* XXX ??? - why isn't this before the "if" statement */
13406 cmdloop(1);
13407 }
13408#if PROFILE
13409 monitor(0);
13410#endif
13411#ifdef GPROF
13412 {
13413 extern void _mcleanup(void);
13414 _mcleanup();
13415 }
13416#endif
13417 exitshell();
13418 /* NOTREACHED */
13419}
13420
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013421#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013422const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013423int main(int argc, char **argv)
13424{
13425 return ash_main(argc, argv);
13426}
13427#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013428
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013429
Eric Andersendf82f612001-06-28 07:46:40 +000013430/*-
13431 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013432 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013433 *
13434 * This code is derived from software contributed to Berkeley by
13435 * Kenneth Almquist.
13436 *
13437 * Redistribution and use in source and binary forms, with or without
13438 * modification, are permitted provided that the following conditions
13439 * are met:
13440 * 1. Redistributions of source code must retain the above copyright
13441 * notice, this list of conditions and the following disclaimer.
13442 * 2. Redistributions in binary form must reproduce the above copyright
13443 * notice, this list of conditions and the following disclaimer in the
13444 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013445 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013446 * may be used to endorse or promote products derived from this software
13447 * without specific prior written permission.
13448 *
13449 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13450 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13451 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13452 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13453 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13454 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13455 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13456 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13457 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13458 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13459 * SUCH DAMAGE.
13460 */