blob: 6f675ce69d571949732a53d3ca5d72f2d08f99fb [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;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000276 sigset_t mask;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000277
278 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000279 /* Signal is not automatically unmasked after it is raised,
280 * do it ourself - unmask all signals */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000281 sigemptyset(&mask);
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000282 sigprocmask(SIG_SETMASK, &mask, NULL);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000283 /* pendingsig = 0; - now done in onsig() */
284
Denis Vlasenkob012b102007-02-19 22:43:01 +0000285 i = EXSIG;
286 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
287 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000288 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000289 signal(SIGINT, SIG_DFL);
290 raise(SIGINT);
291 }
292 i = EXINT;
293 }
294 raise_exception(i);
295 /* NOTREACHED */
296}
297
298#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000299static void
300int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000301{
302 if (--suppressint == 0 && intpending) {
303 raise_interrupt();
304 }
305}
306#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000307static void
308force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000309{
310 suppressint = 0;
311 if (intpending)
312 raise_interrupt();
313}
314#define FORCE_INT_ON force_int_on()
315#else
316#define INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000317 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000318 xbarrier(); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000319 if (--suppressint == 0 && intpending) \
320 raise_interrupt(); \
321 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000322#define FORCE_INT_ON \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000323 do { \
Denis Vlasenkob012b102007-02-19 22:43:01 +0000324 xbarrier(); \
325 suppressint = 0; \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000326 if (intpending) \
327 raise_interrupt(); \
328 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000329#endif /* ASH_OPTIMIZE_FOR_SIZE */
330
331#define SAVE_INT(v) ((v) = suppressint)
332
333#define RESTORE_INT(v) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000334 do { \
Glenn L McGrath2f325a02004-08-06 01:49:04 +0000335 xbarrier(); \
Denis Vlasenko5cedb752007-02-18 19:56:41 +0000336 suppressint = (v); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000337 if (suppressint == 0 && intpending) \
338 raise_interrupt(); \
339 } while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000341/*
342 * Ignore a signal. Only one usage site - in forkchild()
343 */
344static void
345ignoresig(int signo)
346{
347 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
348 signal(signo, SIG_IGN);
349 }
350 sigmode[signo - 1] = S_HARD_IGN;
351}
352
353/*
354 * Signal handler. Only one usage site - in setsignal()
355 */
356static void
357onsig(int signo)
358{
359 gotsig[signo - 1] = 1;
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000360 pendingsig = signo;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000361
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000362 if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000363 if (!suppressint) {
364 pendingsig = 0;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000365 raise_interrupt(); /* does not return */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000366 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000367 intpending = 1;
368 }
369}
370
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000371
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000372/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000373
Eric Andersenc470f442003-07-28 09:56:35 +0000374static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000376{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000377 INT_OFF;
378 fputs(p, file);
379 INT_ON;
380}
381
382static void
383flush_stdout_stderr(void)
384{
385 INT_OFF;
386 fflush(stdout);
387 fflush(stderr);
388 INT_ON;
389}
390
391static void
392flush_stderr(void)
393{
394 INT_OFF;
395 fflush(stderr);
396 INT_ON;
397}
398
399static void
400outcslow(int c, FILE *dest)
401{
402 INT_OFF;
403 putc(c, dest);
404 fflush(dest);
405 INT_ON;
406}
407
408static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
409static int
410out1fmt(const char *fmt, ...)
411{
412 va_list ap;
413 int r;
414
415 INT_OFF;
416 va_start(ap, fmt);
417 r = vprintf(fmt, ap);
418 va_end(ap);
419 INT_ON;
420 return r;
421}
422
423static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
424static int
425fmtstr(char *outbuf, size_t length, const char *fmt, ...)
426{
427 va_list ap;
428 int ret;
429
430 va_start(ap, fmt);
431 INT_OFF;
432 ret = vsnprintf(outbuf, length, fmt, ap);
433 va_end(ap);
434 INT_ON;
435 return ret;
436}
437
438static void
439out1str(const char *p)
440{
441 outstr(p, stdout);
442}
443
444static void
445out2str(const char *p)
446{
447 outstr(p, stderr);
448 flush_stderr();
449}
450
451
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000452/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000453
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000454/* control characters in argument strings */
455#define CTLESC '\201' /* escape next character */
456#define CTLVAR '\202' /* variable defn */
457#define CTLENDVAR '\203'
458#define CTLBACKQ '\204'
459#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
460/* CTLBACKQ | CTLQUOTE == '\205' */
461#define CTLARI '\206' /* arithmetic expression */
462#define CTLENDARI '\207'
463#define CTLQUOTEMARK '\210'
464
465/* variable substitution byte (follows CTLVAR) */
466#define VSTYPE 0x0f /* type of variable substitution */
467#define VSNUL 0x10 /* colon--treat the empty string as unset */
468#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
469
470/* values of VSTYPE field */
471#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
472#define VSMINUS 0x2 /* ${var-text} */
473#define VSPLUS 0x3 /* ${var+text} */
474#define VSQUESTION 0x4 /* ${var?message} */
475#define VSASSIGN 0x5 /* ${var=text} */
476#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
477#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
478#define VSTRIMLEFT 0x8 /* ${var#pattern} */
479#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
480#define VSLENGTH 0xa /* ${#var} */
481
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000482static const char dolatstr[] ALIGN1 = {
483 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
484};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000485
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000486#define NCMD 0
487#define NPIPE 1
488#define NREDIR 2
489#define NBACKGND 3
490#define NSUBSHELL 4
491#define NAND 5
492#define NOR 6
493#define NSEMI 7
494#define NIF 8
495#define NWHILE 9
496#define NUNTIL 10
497#define NFOR 11
498#define NCASE 12
499#define NCLIST 13
500#define NDEFUN 14
501#define NARG 15
502#define NTO 16
503#define NCLOBBER 17
504#define NFROM 18
505#define NFROMTO 19
506#define NAPPEND 20
507#define NTOFD 21
508#define NFROMFD 22
509#define NHERE 23
510#define NXHERE 24
511#define NNOT 25
512
513union node;
514
515struct ncmd {
516 int type;
517 union node *assign;
518 union node *args;
519 union node *redirect;
520};
521
522struct npipe {
523 int type;
524 int backgnd;
525 struct nodelist *cmdlist;
526};
527
528struct nredir {
529 int type;
530 union node *n;
531 union node *redirect;
532};
533
534struct nbinary {
535 int type;
536 union node *ch1;
537 union node *ch2;
538};
539
540struct nif {
541 int type;
542 union node *test;
543 union node *ifpart;
544 union node *elsepart;
545};
546
547struct nfor {
548 int type;
549 union node *args;
550 union node *body;
551 char *var;
552};
553
554struct ncase {
555 int type;
556 union node *expr;
557 union node *cases;
558};
559
560struct nclist {
561 int type;
562 union node *next;
563 union node *pattern;
564 union node *body;
565};
566
567struct narg {
568 int type;
569 union node *next;
570 char *text;
571 struct nodelist *backquote;
572};
573
574struct nfile {
575 int type;
576 union node *next;
577 int fd;
578 union node *fname;
579 char *expfname;
580};
581
582struct ndup {
583 int type;
584 union node *next;
585 int fd;
586 int dupfd;
587 union node *vname;
588};
589
590struct nhere {
591 int type;
592 union node *next;
593 int fd;
594 union node *doc;
595};
596
597struct nnot {
598 int type;
599 union node *com;
600};
601
602union node {
603 int type;
604 struct ncmd ncmd;
605 struct npipe npipe;
606 struct nredir nredir;
607 struct nbinary nbinary;
608 struct nif nif;
609 struct nfor nfor;
610 struct ncase ncase;
611 struct nclist nclist;
612 struct narg narg;
613 struct nfile nfile;
614 struct ndup ndup;
615 struct nhere nhere;
616 struct nnot nnot;
617};
618
619struct nodelist {
620 struct nodelist *next;
621 union node *n;
622};
623
624struct funcnode {
625 int count;
626 union node n;
627};
628
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000629/*
630 * Free a parse tree.
631 */
632static void
633freefunc(struct funcnode *f)
634{
635 if (f && --f->count < 0)
636 free(f);
637}
638
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000639
640/* ============ Debugging output */
641
642#if DEBUG
643
644static FILE *tracefile;
645
646static void
647trace_printf(const char *fmt, ...)
648{
649 va_list va;
650
651 if (debug != 1)
652 return;
653 va_start(va, fmt);
654 vfprintf(tracefile, fmt, va);
655 va_end(va);
656}
657
658static void
659trace_vprintf(const char *fmt, va_list va)
660{
661 if (debug != 1)
662 return;
663 vfprintf(tracefile, fmt, va);
664}
665
666static void
667trace_puts(const char *s)
668{
669 if (debug != 1)
670 return;
671 fputs(s, tracefile);
672}
673
674static void
675trace_puts_quoted(char *s)
676{
677 char *p;
678 char c;
679
680 if (debug != 1)
681 return;
682 putc('"', tracefile);
683 for (p = s; *p; p++) {
684 switch (*p) {
685 case '\n': c = 'n'; goto backslash;
686 case '\t': c = 't'; goto backslash;
687 case '\r': c = 'r'; goto backslash;
688 case '"': c = '"'; goto backslash;
689 case '\\': c = '\\'; goto backslash;
690 case CTLESC: c = 'e'; goto backslash;
691 case CTLVAR: c = 'v'; goto backslash;
692 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
693 case CTLBACKQ: c = 'q'; goto backslash;
694 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
695 backslash:
696 putc('\\', tracefile);
697 putc(c, tracefile);
698 break;
699 default:
700 if (*p >= ' ' && *p <= '~')
701 putc(*p, tracefile);
702 else {
703 putc('\\', tracefile);
704 putc(*p >> 6 & 03, tracefile);
705 putc(*p >> 3 & 07, tracefile);
706 putc(*p & 07, tracefile);
707 }
708 break;
709 }
710 }
711 putc('"', tracefile);
712}
713
714static void
715trace_puts_args(char **ap)
716{
717 if (debug != 1)
718 return;
719 if (!*ap)
720 return;
721 while (1) {
722 trace_puts_quoted(*ap);
723 if (!*++ap) {
724 putc('\n', tracefile);
725 break;
726 }
727 putc(' ', tracefile);
728 }
729}
730
731static void
732opentrace(void)
733{
734 char s[100];
735#ifdef O_APPEND
736 int flags;
737#endif
738
739 if (debug != 1) {
740 if (tracefile)
741 fflush(tracefile);
742 /* leave open because libedit might be using it */
743 return;
744 }
745 strcpy(s, "./trace");
746 if (tracefile) {
747 if (!freopen(s, "a", tracefile)) {
748 fprintf(stderr, "Can't re-open %s\n", s);
749 debug = 0;
750 return;
751 }
752 } else {
753 tracefile = fopen(s, "a");
754 if (tracefile == NULL) {
755 fprintf(stderr, "Can't open %s\n", s);
756 debug = 0;
757 return;
758 }
759 }
760#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000761 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000762 if (flags >= 0)
763 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
764#endif
765 setlinebuf(tracefile);
766 fputs("\nTracing started.\n", tracefile);
767}
768
769static void
770indent(int amount, char *pfx, FILE *fp)
771{
772 int i;
773
774 for (i = 0; i < amount; i++) {
775 if (pfx && i == amount - 1)
776 fputs(pfx, fp);
777 putc('\t', fp);
778 }
779}
780
781/* little circular references here... */
782static void shtree(union node *n, int ind, char *pfx, FILE *fp);
783
784static void
785sharg(union node *arg, FILE *fp)
786{
787 char *p;
788 struct nodelist *bqlist;
789 int subtype;
790
791 if (arg->type != NARG) {
792 out1fmt("<node type %d>\n", arg->type);
793 abort();
794 }
795 bqlist = arg->narg.backquote;
796 for (p = arg->narg.text; *p; p++) {
797 switch (*p) {
798 case CTLESC:
799 putc(*++p, fp);
800 break;
801 case CTLVAR:
802 putc('$', fp);
803 putc('{', fp);
804 subtype = *++p;
805 if (subtype == VSLENGTH)
806 putc('#', fp);
807
808 while (*p != '=')
809 putc(*p++, fp);
810
811 if (subtype & VSNUL)
812 putc(':', fp);
813
814 switch (subtype & VSTYPE) {
815 case VSNORMAL:
816 putc('}', fp);
817 break;
818 case VSMINUS:
819 putc('-', fp);
820 break;
821 case VSPLUS:
822 putc('+', fp);
823 break;
824 case VSQUESTION:
825 putc('?', fp);
826 break;
827 case VSASSIGN:
828 putc('=', fp);
829 break;
830 case VSTRIMLEFT:
831 putc('#', fp);
832 break;
833 case VSTRIMLEFTMAX:
834 putc('#', fp);
835 putc('#', fp);
836 break;
837 case VSTRIMRIGHT:
838 putc('%', fp);
839 break;
840 case VSTRIMRIGHTMAX:
841 putc('%', fp);
842 putc('%', fp);
843 break;
844 case VSLENGTH:
845 break;
846 default:
847 out1fmt("<subtype %d>", subtype);
848 }
849 break;
850 case CTLENDVAR:
851 putc('}', fp);
852 break;
853 case CTLBACKQ:
854 case CTLBACKQ|CTLQUOTE:
855 putc('$', fp);
856 putc('(', fp);
857 shtree(bqlist->n, -1, NULL, fp);
858 putc(')', fp);
859 break;
860 default:
861 putc(*p, fp);
862 break;
863 }
864 }
865}
866
867static void
868shcmd(union node *cmd, FILE *fp)
869{
870 union node *np;
871 int first;
872 const char *s;
873 int dftfd;
874
875 first = 1;
876 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000877 if (!first)
878 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000879 sharg(np, fp);
880 first = 0;
881 }
882 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000883 if (!first)
884 putc(' ', fp);
885 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000886 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000887 case NTO: s = ">>"+1; dftfd = 1; break;
888 case NCLOBBER: s = ">|"; dftfd = 1; break;
889 case NAPPEND: s = ">>"; dftfd = 1; break;
890 case NTOFD: s = ">&"; dftfd = 1; break;
891 case NFROM: s = "<"; break;
892 case NFROMFD: s = "<&"; break;
893 case NFROMTO: s = "<>"; break;
894 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000895 }
896 if (np->nfile.fd != dftfd)
897 fprintf(fp, "%d", np->nfile.fd);
898 fputs(s, fp);
899 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
900 fprintf(fp, "%d", np->ndup.dupfd);
901 } else {
902 sharg(np->nfile.fname, fp);
903 }
904 first = 0;
905 }
906}
907
908static void
909shtree(union node *n, int ind, char *pfx, FILE *fp)
910{
911 struct nodelist *lp;
912 const char *s;
913
914 if (n == NULL)
915 return;
916
917 indent(ind, pfx, fp);
918 switch (n->type) {
919 case NSEMI:
920 s = "; ";
921 goto binop;
922 case NAND:
923 s = " && ";
924 goto binop;
925 case NOR:
926 s = " || ";
927 binop:
928 shtree(n->nbinary.ch1, ind, NULL, fp);
929 /* if (ind < 0) */
930 fputs(s, fp);
931 shtree(n->nbinary.ch2, ind, NULL, fp);
932 break;
933 case NCMD:
934 shcmd(n, fp);
935 if (ind >= 0)
936 putc('\n', fp);
937 break;
938 case NPIPE:
939 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
940 shcmd(lp->n, fp);
941 if (lp->next)
942 fputs(" | ", fp);
943 }
944 if (n->npipe.backgnd)
945 fputs(" &", fp);
946 if (ind >= 0)
947 putc('\n', fp);
948 break;
949 default:
950 fprintf(fp, "<node type %d>", n->type);
951 if (ind >= 0)
952 putc('\n', fp);
953 break;
954 }
955}
956
957static void
958showtree(union node *n)
959{
960 trace_puts("showtree called\n");
961 shtree(n, 1, NULL, stdout);
962}
963
964#define TRACE(param) trace_printf param
965#define TRACEV(param) trace_vprintf param
966
967#else
968
969#define TRACE(param)
970#define TRACEV(param)
971
972#endif /* DEBUG */
973
974
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000975/* ============ Parser data */
976
977/*
Denis Vlasenkob012b102007-02-19 22:43:01 +0000978 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
979 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000980struct strlist {
981 struct strlist *next;
982 char *text;
983};
984
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000985#if ENABLE_ASH_ALIAS
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000986struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000987#endif
988
Denis Vlasenkob012b102007-02-19 22:43:01 +0000989struct strpush {
990 struct strpush *prev; /* preceding string on stack */
991 char *prevstring;
992 int prevnleft;
993#if ENABLE_ASH_ALIAS
994 struct alias *ap; /* if push was associated with an alias */
995#endif
996 char *string; /* remember the string since it may change */
997};
998
999struct parsefile {
1000 struct parsefile *prev; /* preceding file on stack */
1001 int linno; /* current line */
1002 int fd; /* file descriptor (or -1 if string) */
1003 int nleft; /* number of chars left in this line */
1004 int lleft; /* number of chars left in this buffer */
1005 char *nextc; /* next char in buffer */
1006 char *buf; /* input buffer */
1007 struct strpush *strpush; /* for pushing strings at this level */
1008 struct strpush basestrpush; /* so pushing one is fast */
1009};
1010
1011static struct parsefile basepf; /* top level input file */
1012static struct parsefile *parsefile = &basepf; /* current input file */
1013static int startlinno; /* line # where last token started */
1014static char *commandname; /* currently executing command */
1015static struct strlist *cmdenviron; /* environment for builtin command */
1016static int exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001017
1018
1019/* ============ Message printing */
1020
1021static void
1022ash_vmsg(const char *msg, va_list ap)
1023{
1024 fprintf(stderr, "%s: ", arg0);
1025 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001026 if (strcmp(arg0, commandname))
1027 fprintf(stderr, "%s: ", commandname);
1028 if (!iflag || parsefile->fd)
1029 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001030 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001031 vfprintf(stderr, msg, ap);
1032 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001033}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001034
1035/*
1036 * Exverror is called to raise the error exception. If the second argument
1037 * is not NULL then error prints an error message using printf style
1038 * formatting. It then raises the error exception.
1039 */
1040static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
1041static void
1042ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001043{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001044#if DEBUG
1045 if (msg) {
1046 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1047 TRACEV((msg, ap));
1048 TRACE(("\") pid=%d\n", getpid()));
1049 } else
1050 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1051 if (msg)
1052#endif
1053 ash_vmsg(msg, ap);
1054
1055 flush_stdout_stderr();
1056 raise_exception(cond);
1057 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001058}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001059
1060static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
1061static void
1062ash_msg_and_raise_error(const char *msg, ...)
1063{
1064 va_list ap;
1065
1066 va_start(ap, msg);
1067 ash_vmsg_and_raise(EXERROR, msg, ap);
1068 /* NOTREACHED */
1069 va_end(ap);
1070}
1071
1072static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
1073static void
1074ash_msg_and_raise(int cond, const char *msg, ...)
1075{
1076 va_list ap;
1077
1078 va_start(ap, msg);
1079 ash_vmsg_and_raise(cond, msg, ap);
1080 /* NOTREACHED */
1081 va_end(ap);
1082}
1083
1084/*
1085 * error/warning routines for external builtins
1086 */
1087static void
1088ash_msg(const char *fmt, ...)
1089{
1090 va_list ap;
1091
1092 va_start(ap, fmt);
1093 ash_vmsg(fmt, ap);
1094 va_end(ap);
1095}
1096
1097/*
1098 * Return a string describing an error. The returned string may be a
1099 * pointer to a static buffer that will be overwritten on the next call.
1100 * Action describes the operation that got the error.
1101 */
1102static const char *
1103errmsg(int e, const char *em)
1104{
1105 if (e == ENOENT || e == ENOTDIR) {
1106 return em;
1107 }
1108 return strerror(e);
1109}
1110
1111
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001112/* ============ Memory allocation */
1113
1114/*
1115 * It appears that grabstackstr() will barf with such alignments
1116 * because stalloc() will return a string allocated in a new stackblock.
1117 */
1118#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1119enum {
1120 /* Most machines require the value returned from malloc to be aligned
1121 * in some way. The following macro will get this right
1122 * on many machines. */
1123 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1124 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001125 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001126};
1127
1128struct stack_block {
1129 struct stack_block *prev;
1130 char space[MINSIZE];
1131};
1132
1133struct stackmark {
1134 struct stack_block *stackp;
1135 char *stacknxt;
1136 size_t stacknleft;
1137 struct stackmark *marknext;
1138};
1139
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001140
Denis Vlasenko01631112007-12-16 17:20:38 +00001141struct globals_memstack {
1142 struct stack_block *g_stackp; // = &stackbase;
1143 struct stackmark *markp;
1144 char *g_stacknxt; // = stackbase.space;
1145 char *sstrend; // = stackbase.space + MINSIZE;
1146 size_t g_stacknleft; // = MINSIZE;
1147 int herefd; // = -1;
1148 struct stack_block stackbase;
1149};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001150extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1151#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001152#define g_stackp (G_memstack.g_stackp )
1153#define markp (G_memstack.markp )
1154#define g_stacknxt (G_memstack.g_stacknxt )
1155#define sstrend (G_memstack.sstrend )
1156#define g_stacknleft (G_memstack.g_stacknleft)
1157#define herefd (G_memstack.herefd )
1158#define stackbase (G_memstack.stackbase )
1159#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001160 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1161 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001162 g_stackp = &stackbase; \
1163 g_stacknxt = stackbase.space; \
1164 g_stacknleft = MINSIZE; \
1165 sstrend = stackbase.space + MINSIZE; \
1166 herefd = -1; \
1167} while (0)
1168
1169#define stackblock() ((void *)g_stacknxt)
1170#define stackblocksize() g_stacknleft
1171
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001172
1173static void *
1174ckrealloc(void * p, size_t nbytes)
1175{
1176 p = realloc(p, nbytes);
1177 if (!p)
1178 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1179 return p;
1180}
1181
1182static void *
1183ckmalloc(size_t nbytes)
1184{
1185 return ckrealloc(NULL, nbytes);
1186}
1187
Denis Vlasenko597906c2008-02-20 16:38:54 +00001188static void *
1189ckzalloc(size_t nbytes)
1190{
1191 return memset(ckmalloc(nbytes), 0, nbytes);
1192}
1193
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001194/*
1195 * Make a copy of a string in safe storage.
1196 */
1197static char *
1198ckstrdup(const char *s)
1199{
1200 char *p = strdup(s);
1201 if (!p)
1202 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1203 return p;
1204}
1205
1206/*
1207 * Parse trees for commands are allocated in lifo order, so we use a stack
1208 * to make this more efficient, and also to avoid all sorts of exception
1209 * handling code to handle interrupts in the middle of a parse.
1210 *
1211 * The size 504 was chosen because the Ultrix malloc handles that size
1212 * well.
1213 */
1214static void *
1215stalloc(size_t nbytes)
1216{
1217 char *p;
1218 size_t aligned;
1219
1220 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001221 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001222 size_t len;
1223 size_t blocksize;
1224 struct stack_block *sp;
1225
1226 blocksize = aligned;
1227 if (blocksize < MINSIZE)
1228 blocksize = MINSIZE;
1229 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1230 if (len < blocksize)
1231 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1232 INT_OFF;
1233 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001234 sp->prev = g_stackp;
1235 g_stacknxt = sp->space;
1236 g_stacknleft = blocksize;
1237 sstrend = g_stacknxt + blocksize;
1238 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001239 INT_ON;
1240 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001241 p = g_stacknxt;
1242 g_stacknxt += aligned;
1243 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001244 return p;
1245}
1246
Denis Vlasenko597906c2008-02-20 16:38:54 +00001247static void *
1248stzalloc(size_t nbytes)
1249{
1250 return memset(stalloc(nbytes), 0, nbytes);
1251}
1252
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001253static void
1254stunalloc(void *p)
1255{
1256#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001257 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001258 write(2, "stunalloc\n", 10);
1259 abort();
1260 }
1261#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001262 g_stacknleft += g_stacknxt - (char *)p;
1263 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001264}
1265
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001266/*
1267 * Like strdup but works with the ash stack.
1268 */
1269static char *
1270ststrdup(const char *p)
1271{
1272 size_t len = strlen(p) + 1;
1273 return memcpy(stalloc(len), p, len);
1274}
1275
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001276static void
1277setstackmark(struct stackmark *mark)
1278{
Denis Vlasenko01631112007-12-16 17:20:38 +00001279 mark->stackp = g_stackp;
1280 mark->stacknxt = g_stacknxt;
1281 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001282 mark->marknext = markp;
1283 markp = mark;
1284}
1285
1286static void
1287popstackmark(struct stackmark *mark)
1288{
1289 struct stack_block *sp;
1290
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001291 if (!mark->stackp)
1292 return;
1293
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001294 INT_OFF;
1295 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001296 while (g_stackp != mark->stackp) {
1297 sp = g_stackp;
1298 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001299 free(sp);
1300 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001301 g_stacknxt = mark->stacknxt;
1302 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001303 sstrend = mark->stacknxt + mark->stacknleft;
1304 INT_ON;
1305}
1306
1307/*
1308 * When the parser reads in a string, it wants to stick the string on the
1309 * stack and only adjust the stack pointer when it knows how big the
1310 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1311 * of space on top of the stack and stackblocklen returns the length of
1312 * this block. Growstackblock will grow this space by at least one byte,
1313 * possibly moving it (like realloc). Grabstackblock actually allocates the
1314 * part of the block that has been used.
1315 */
1316static void
1317growstackblock(void)
1318{
1319 size_t newlen;
1320
Denis Vlasenko01631112007-12-16 17:20:38 +00001321 newlen = g_stacknleft * 2;
1322 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001323 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1324 if (newlen < 128)
1325 newlen += 128;
1326
Denis Vlasenko01631112007-12-16 17:20:38 +00001327 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001328 struct stack_block *oldstackp;
1329 struct stackmark *xmark;
1330 struct stack_block *sp;
1331 struct stack_block *prevstackp;
1332 size_t grosslen;
1333
1334 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001335 oldstackp = g_stackp;
1336 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001337 prevstackp = sp->prev;
1338 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1339 sp = ckrealloc(sp, grosslen);
1340 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001341 g_stackp = sp;
1342 g_stacknxt = sp->space;
1343 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001344 sstrend = sp->space + newlen;
1345
1346 /*
1347 * Stack marks pointing to the start of the old block
1348 * must be relocated to point to the new block
1349 */
1350 xmark = markp;
1351 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001352 xmark->stackp = g_stackp;
1353 xmark->stacknxt = g_stacknxt;
1354 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001355 xmark = xmark->marknext;
1356 }
1357 INT_ON;
1358 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001359 char *oldspace = g_stacknxt;
1360 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001361 char *p = stalloc(newlen);
1362
1363 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001364 g_stacknxt = memcpy(p, oldspace, oldlen);
1365 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001366 }
1367}
1368
1369static void
1370grabstackblock(size_t len)
1371{
1372 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001373 g_stacknxt += len;
1374 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001375}
1376
1377/*
1378 * The following routines are somewhat easier to use than the above.
1379 * The user declares a variable of type STACKSTR, which may be declared
1380 * to be a register. The macro STARTSTACKSTR initializes things. Then
1381 * the user uses the macro STPUTC to add characters to the string. In
1382 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1383 * grown as necessary. When the user is done, she can just leave the
1384 * string there and refer to it using stackblock(). Or she can allocate
1385 * the space for it using grabstackstr(). If it is necessary to allow
1386 * someone else to use the stack temporarily and then continue to grow
1387 * the string, the user should use grabstack to allocate the space, and
1388 * then call ungrabstr(p) to return to the previous mode of operation.
1389 *
1390 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1391 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1392 * is space for at least one character.
1393 */
1394static void *
1395growstackstr(void)
1396{
1397 size_t len = stackblocksize();
1398 if (herefd >= 0 && len >= 1024) {
1399 full_write(herefd, stackblock(), len);
1400 return stackblock();
1401 }
1402 growstackblock();
1403 return stackblock() + len;
1404}
1405
1406/*
1407 * Called from CHECKSTRSPACE.
1408 */
1409static char *
1410makestrspace(size_t newlen, char *p)
1411{
Denis Vlasenko01631112007-12-16 17:20:38 +00001412 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001413 size_t size = stackblocksize();
1414
1415 for (;;) {
1416 size_t nleft;
1417
1418 size = stackblocksize();
1419 nleft = size - len;
1420 if (nleft >= newlen)
1421 break;
1422 growstackblock();
1423 }
1424 return stackblock() + len;
1425}
1426
1427static char *
1428stack_nputstr(const char *s, size_t n, char *p)
1429{
1430 p = makestrspace(n, p);
1431 p = memcpy(p, s, n) + n;
1432 return p;
1433}
1434
1435static char *
1436stack_putstr(const char *s, char *p)
1437{
1438 return stack_nputstr(s, strlen(s), p);
1439}
1440
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001441static char *
1442_STPUTC(int c, char *p)
1443{
1444 if (p == sstrend)
1445 p = growstackstr();
1446 *p++ = c;
1447 return p;
1448}
1449
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001450#define STARTSTACKSTR(p) ((p) = stackblock())
1451#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001452#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001453 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001454 char *q = (p); \
1455 size_t l = (n); \
1456 size_t m = sstrend - q; \
1457 if (l > m) \
1458 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001459 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001460#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001461#define STACKSTRNUL(p) \
1462 do { \
1463 if ((p) == sstrend) \
1464 p = growstackstr(); \
1465 *p = '\0'; \
1466 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001467#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001468#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001469#define STADJUST(amount, p) (p += (amount))
1470
1471#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1472#define ungrabstackstr(s, p) stunalloc((s))
1473#define stackstrend() ((void *)sstrend)
1474
1475
1476/* ============ String helpers */
1477
1478/*
1479 * prefix -- see if pfx is a prefix of string.
1480 */
1481static char *
1482prefix(const char *string, const char *pfx)
1483{
1484 while (*pfx) {
1485 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001486 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001487 }
1488 return (char *) string;
1489}
1490
1491/*
1492 * Check for a valid number. This should be elsewhere.
1493 */
1494static int
1495is_number(const char *p)
1496{
1497 do {
1498 if (!isdigit(*p))
1499 return 0;
1500 } while (*++p != '\0');
1501 return 1;
1502}
1503
1504/*
1505 * Convert a string of digits to an integer, printing an error message on
1506 * failure.
1507 */
1508static int
1509number(const char *s)
1510{
1511 if (!is_number(s))
1512 ash_msg_and_raise_error(illnum, s);
1513 return atoi(s);
1514}
1515
1516/*
1517 * Produce a possibly single quoted string suitable as input to the shell.
1518 * The return string is allocated on the stack.
1519 */
1520static char *
1521single_quote(const char *s)
1522{
1523 char *p;
1524
1525 STARTSTACKSTR(p);
1526
1527 do {
1528 char *q;
1529 size_t len;
1530
1531 len = strchrnul(s, '\'') - s;
1532
1533 q = p = makestrspace(len + 3, p);
1534
1535 *q++ = '\'';
1536 q = memcpy(q, s, len) + len;
1537 *q++ = '\'';
1538 s += len;
1539
1540 STADJUST(q - p, p);
1541
1542 len = strspn(s, "'");
1543 if (!len)
1544 break;
1545
1546 q = p = makestrspace(len + 3, p);
1547
1548 *q++ = '"';
1549 q = memcpy(q, s, len) + len;
1550 *q++ = '"';
1551 s += len;
1552
1553 STADJUST(q - p, p);
1554 } while (*s);
1555
1556 USTPUTC(0, p);
1557
1558 return stackblock();
1559}
1560
1561
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001562/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001563
1564static char **argptr; /* argument list for builtin commands */
1565static char *optionarg; /* set by nextopt (like getopt) */
1566static char *optptr; /* used by nextopt */
1567
1568/*
1569 * XXX - should get rid of. have all builtins use getopt(3). the
1570 * library getopt must have the BSD extension static variable "optreset"
1571 * otherwise it can't be used within the shell safely.
1572 *
1573 * Standard option processing (a la getopt) for builtin routines. The
1574 * only argument that is passed to nextopt is the option string; the
1575 * other arguments are unnecessary. It return the character, or '\0' on
1576 * end of input.
1577 */
1578static int
1579nextopt(const char *optstring)
1580{
1581 char *p;
1582 const char *q;
1583 char c;
1584
1585 p = optptr;
1586 if (p == NULL || *p == '\0') {
1587 p = *argptr;
1588 if (p == NULL || *p != '-' || *++p == '\0')
1589 return '\0';
1590 argptr++;
1591 if (LONE_DASH(p)) /* check for "--" */
1592 return '\0';
1593 }
1594 c = *p++;
1595 for (q = optstring; *q != c; ) {
1596 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001597 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001598 if (*++q == ':')
1599 q++;
1600 }
1601 if (*++q == ':') {
1602 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001603 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001604 optionarg = p;
1605 p = NULL;
1606 }
1607 optptr = p;
1608 return c;
1609}
1610
1611
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001612/* ============ Math support definitions */
1613
1614#if ENABLE_ASH_MATH_SUPPORT_64
1615typedef int64_t arith_t;
1616#define arith_t_type long long
1617#else
1618typedef long arith_t;
1619#define arith_t_type long
1620#endif
1621
1622#if ENABLE_ASH_MATH_SUPPORT
1623static arith_t dash_arith(const char *);
1624static arith_t arith(const char *expr, int *perrcode);
1625#endif
1626
1627#if ENABLE_ASH_RANDOM_SUPPORT
1628static unsigned long rseed;
1629#ifndef DYNAMIC_VAR
1630#define DYNAMIC_VAR
1631#endif
1632#endif
1633
1634
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001635/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001636
Denis Vlasenko01631112007-12-16 17:20:38 +00001637/*
1638 * The parsefile structure pointed to by the global variable parsefile
1639 * contains information about the current file being read.
1640 */
1641struct redirtab {
1642 struct redirtab *next;
1643 int renamed[10];
1644 int nullredirs;
1645};
1646
1647struct shparam {
1648 int nparam; /* # of positional parameters (without $0) */
1649#if ENABLE_ASH_GETOPTS
1650 int optind; /* next parameter to be processed by getopts */
1651 int optoff; /* used by getopts */
1652#endif
1653 unsigned char malloced; /* if parameter list dynamically allocated */
1654 char **p; /* parameter list */
1655};
1656
1657/*
1658 * Free the list of positional parameters.
1659 */
1660static void
1661freeparam(volatile struct shparam *param)
1662{
1663 char **ap;
1664
1665 if (param->malloced) {
1666 for (ap = param->p; *ap; ap++)
1667 free(*ap);
1668 free(param->p);
1669 }
1670}
1671
1672#if ENABLE_ASH_GETOPTS
1673static void getoptsreset(const char *value);
1674#endif
1675
1676struct var {
1677 struct var *next; /* next entry in hash list */
1678 int flags; /* flags are defined above */
1679 const char *text; /* name=value */
1680 void (*func)(const char *); /* function to be called when */
1681 /* the variable gets set/unset */
1682};
1683
1684struct localvar {
1685 struct localvar *next; /* next local variable in list */
1686 struct var *vp; /* the variable that was made local */
1687 int flags; /* saved flags */
1688 const char *text; /* saved text */
1689};
1690
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001691/* flags */
1692#define VEXPORT 0x01 /* variable is exported */
1693#define VREADONLY 0x02 /* variable cannot be modified */
1694#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1695#define VTEXTFIXED 0x08 /* text is statically allocated */
1696#define VSTACK 0x10 /* text is allocated on the stack */
1697#define VUNSET 0x20 /* the variable is not set */
1698#define VNOFUNC 0x40 /* don't call the callback function */
1699#define VNOSET 0x80 /* do not set variable - just readonly test */
1700#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1701#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001702# define VDYNAMIC 0x200 /* dynamic variable */
1703#else
1704# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001705#endif
1706
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001707#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001708static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001709#define defifs (defifsvar + 4)
1710#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001711static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001712#endif
1713
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001714
Denis Vlasenko01631112007-12-16 17:20:38 +00001715/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001716#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001717static void
1718change_lc_all(const char *value)
1719{
1720 if (value && *value != '\0')
1721 setlocale(LC_ALL, value);
1722}
1723static void
1724change_lc_ctype(const char *value)
1725{
1726 if (value && *value != '\0')
1727 setlocale(LC_CTYPE, value);
1728}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001729#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001730#if ENABLE_ASH_MAIL
1731static void chkmail(void);
1732static void changemail(const char *);
1733#endif
1734static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#if ENABLE_ASH_RANDOM_SUPPORT
1736static void change_random(const char *);
1737#endif
1738
Denis Vlasenko01631112007-12-16 17:20:38 +00001739static const struct {
1740 int flags;
1741 const char *text;
1742 void (*func)(const char *);
1743} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001745 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001747 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001748#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001750 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1751 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001752#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001753 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1754 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1755 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1756 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001758 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759#endif
1760#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001761 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001762#endif
1763#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001764 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1765 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001766#endif
1767#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001768 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769#endif
1770};
1771
Denis Vlasenko01631112007-12-16 17:20:38 +00001772
1773struct globals_var {
1774 struct shparam shellparam; /* $@ current positional parameters */
1775 struct redirtab *redirlist;
1776 int g_nullredirs;
1777 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1778 struct var *vartab[VTABSIZE];
1779 struct var varinit[ARRAY_SIZE(varinit_data)];
1780};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001781extern struct globals_var *const ash_ptr_to_globals_var;
1782#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001783#define shellparam (G_var.shellparam )
1784#define redirlist (G_var.redirlist )
1785#define g_nullredirs (G_var.g_nullredirs )
1786#define preverrout_fd (G_var.preverrout_fd)
1787#define vartab (G_var.vartab )
1788#define varinit (G_var.varinit )
1789#define INIT_G_var() do { \
1790 int i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001791 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1792 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001793 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1794 varinit[i].flags = varinit_data[i].flags; \
1795 varinit[i].text = varinit_data[i].text; \
1796 varinit[i].func = varinit_data[i].func; \
1797 } \
1798} while (0)
1799
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001800#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001801#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001802# define vmail (&vifs)[1]
1803# define vmpath (&vmail)[1]
1804# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001806# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001807#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001808#define vps1 (&vpath)[1]
1809#define vps2 (&vps1)[1]
1810#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001811#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001812# define voptind (&vps4)[1]
1813# if ENABLE_ASH_RANDOM_SUPPORT
1814# define vrandom (&voptind)[1]
1815# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001816#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001817# if ENABLE_ASH_RANDOM_SUPPORT
1818# define vrandom (&vps4)[1]
1819# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001820#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001821
1822/*
1823 * The following macros access the values of the above variables.
1824 * They have to skip over the name. They return the null string
1825 * for unset variables.
1826 */
1827#define ifsval() (vifs.text + 4)
1828#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001829#if ENABLE_ASH_MAIL
1830# define mailval() (vmail.text + 5)
1831# define mpathval() (vmpath.text + 9)
1832# define mpathset() ((vmpath.flags & VUNSET) == 0)
1833#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001834#define pathval() (vpath.text + 5)
1835#define ps1val() (vps1.text + 4)
1836#define ps2val() (vps2.text + 4)
1837#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001838#if ENABLE_ASH_GETOPTS
1839# define optindval() (voptind.text + 7)
1840#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001841
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001842
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001843#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1844#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1845
Denis Vlasenko01631112007-12-16 17:20:38 +00001846#if ENABLE_ASH_GETOPTS
1847static void
1848getoptsreset(const char *value)
1849{
1850 shellparam.optind = number(value);
1851 shellparam.optoff = -1;
1852}
1853#endif
1854
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001855/*
1856 * Return of a legal variable name (a letter or underscore followed by zero or
1857 * more letters, underscores, and digits).
1858 */
1859static char *
1860endofname(const char *name)
1861{
1862 char *p;
1863
1864 p = (char *) name;
1865 if (!is_name(*p))
1866 return p;
1867 while (*++p) {
1868 if (!is_in_name(*p))
1869 break;
1870 }
1871 return p;
1872}
1873
1874/*
1875 * Compares two strings up to the first = or '\0'. The first
1876 * string must be terminated by '='; the second may be terminated by
1877 * either '=' or '\0'.
1878 */
1879static int
1880varcmp(const char *p, const char *q)
1881{
1882 int c, d;
1883
1884 while ((c = *p) == (d = *q)) {
1885 if (!c || c == '=')
1886 goto out;
1887 p++;
1888 q++;
1889 }
1890 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001891 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001893 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001894 out:
1895 return c - d;
1896}
1897
1898static int
1899varequal(const char *a, const char *b)
1900{
1901 return !varcmp(a, b);
1902}
1903
1904/*
1905 * Find the appropriate entry in the hash table from the name.
1906 */
1907static struct var **
1908hashvar(const char *p)
1909{
1910 unsigned hashval;
1911
1912 hashval = ((unsigned char) *p) << 4;
1913 while (*p && *p != '=')
1914 hashval += (unsigned char) *p++;
1915 return &vartab[hashval % VTABSIZE];
1916}
1917
1918static int
1919vpcmp(const void *a, const void *b)
1920{
1921 return varcmp(*(const char **)a, *(const char **)b);
1922}
1923
1924/*
1925 * This routine initializes the builtin variables.
1926 */
1927static void
1928initvar(void)
1929{
1930 struct var *vp;
1931 struct var *end;
1932 struct var **vpp;
1933
1934 /*
1935 * PS1 depends on uid
1936 */
1937#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1938 vps1.text = "PS1=\\w \\$ ";
1939#else
1940 if (!geteuid())
1941 vps1.text = "PS1=# ";
1942#endif
1943 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001944 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001945 do {
1946 vpp = hashvar(vp->text);
1947 vp->next = *vpp;
1948 *vpp = vp;
1949 } while (++vp < end);
1950}
1951
1952static struct var **
1953findvar(struct var **vpp, const char *name)
1954{
1955 for (; *vpp; vpp = &(*vpp)->next) {
1956 if (varequal((*vpp)->text, name)) {
1957 break;
1958 }
1959 }
1960 return vpp;
1961}
1962
1963/*
1964 * Find the value of a variable. Returns NULL if not set.
1965 */
1966static char *
1967lookupvar(const char *name)
1968{
1969 struct var *v;
1970
1971 v = *findvar(hashvar(name), name);
1972 if (v) {
1973#ifdef DYNAMIC_VAR
1974 /*
1975 * Dynamic variables are implemented roughly the same way they are
1976 * in bash. Namely, they're "special" so long as they aren't unset.
1977 * As soon as they're unset, they're no longer dynamic, and dynamic
1978 * lookup will no longer happen at that point. -- PFM.
1979 */
1980 if ((v->flags & VDYNAMIC))
1981 (*v->func)(NULL);
1982#endif
1983 if (!(v->flags & VUNSET))
1984 return strchrnul(v->text, '=') + 1;
1985 }
1986 return NULL;
1987}
1988
1989/*
1990 * Search the environment of a builtin command.
1991 */
1992static char *
1993bltinlookup(const char *name)
1994{
1995 struct strlist *sp;
1996
1997 for (sp = cmdenviron; sp; sp = sp->next) {
1998 if (varequal(sp->text, name))
1999 return strchrnul(sp->text, '=') + 1;
2000 }
2001 return lookupvar(name);
2002}
2003
2004/*
2005 * Same as setvar except that the variable and value are passed in
2006 * the first argument as name=value. Since the first argument will
2007 * be actually stored in the table, it should not be a string that
2008 * will go away.
2009 * Called with interrupts off.
2010 */
2011static void
2012setvareq(char *s, int flags)
2013{
2014 struct var *vp, **vpp;
2015
2016 vpp = hashvar(s);
2017 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2018 vp = *findvar(vpp, s);
2019 if (vp) {
2020 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2021 const char *n;
2022
2023 if (flags & VNOSAVE)
2024 free(s);
2025 n = vp->text;
2026 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2027 }
2028
2029 if (flags & VNOSET)
2030 return;
2031
2032 if (vp->func && (flags & VNOFUNC) == 0)
2033 (*vp->func)(strchrnul(s, '=') + 1);
2034
2035 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2036 free((char*)vp->text);
2037
2038 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2039 } else {
2040 if (flags & VNOSET)
2041 return;
2042 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002043 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002044 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002045 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002046 *vpp = vp;
2047 }
2048 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2049 s = ckstrdup(s);
2050 vp->text = s;
2051 vp->flags = flags;
2052}
2053
2054/*
2055 * Set the value of a variable. The flags argument is ored with the
2056 * flags of the variable. If val is NULL, the variable is unset.
2057 */
2058static void
2059setvar(const char *name, const char *val, int flags)
2060{
2061 char *p, *q;
2062 size_t namelen;
2063 char *nameeq;
2064 size_t vallen;
2065
2066 q = endofname(name);
2067 p = strchrnul(q, '=');
2068 namelen = p - name;
2069 if (!namelen || p != q)
2070 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2071 vallen = 0;
2072 if (val == NULL) {
2073 flags |= VUNSET;
2074 } else {
2075 vallen = strlen(val);
2076 }
2077 INT_OFF;
2078 nameeq = ckmalloc(namelen + vallen + 2);
2079 p = memcpy(nameeq, name, namelen) + namelen;
2080 if (val) {
2081 *p++ = '=';
2082 p = memcpy(p, val, vallen) + vallen;
2083 }
2084 *p = '\0';
2085 setvareq(nameeq, flags | VNOSAVE);
2086 INT_ON;
2087}
2088
2089#if ENABLE_ASH_GETOPTS
2090/*
2091 * Safe version of setvar, returns 1 on success 0 on failure.
2092 */
2093static int
2094setvarsafe(const char *name, const char *val, int flags)
2095{
2096 int err;
2097 volatile int saveint;
2098 struct jmploc *volatile savehandler = exception_handler;
2099 struct jmploc jmploc;
2100
2101 SAVE_INT(saveint);
2102 if (setjmp(jmploc.loc))
2103 err = 1;
2104 else {
2105 exception_handler = &jmploc;
2106 setvar(name, val, flags);
2107 err = 0;
2108 }
2109 exception_handler = savehandler;
2110 RESTORE_INT(saveint);
2111 return err;
2112}
2113#endif
2114
2115/*
2116 * Unset the specified variable.
2117 */
2118static int
2119unsetvar(const char *s)
2120{
2121 struct var **vpp;
2122 struct var *vp;
2123 int retval;
2124
2125 vpp = findvar(hashvar(s), s);
2126 vp = *vpp;
2127 retval = 2;
2128 if (vp) {
2129 int flags = vp->flags;
2130
2131 retval = 1;
2132 if (flags & VREADONLY)
2133 goto out;
2134#ifdef DYNAMIC_VAR
2135 vp->flags &= ~VDYNAMIC;
2136#endif
2137 if (flags & VUNSET)
2138 goto ok;
2139 if ((flags & VSTRFIXED) == 0) {
2140 INT_OFF;
2141 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2142 free((char*)vp->text);
2143 *vpp = vp->next;
2144 free(vp);
2145 INT_ON;
2146 } else {
2147 setvar(s, 0, 0);
2148 vp->flags &= ~VEXPORT;
2149 }
2150 ok:
2151 retval = 0;
2152 }
2153 out:
2154 return retval;
2155}
2156
2157/*
2158 * Process a linked list of variable assignments.
2159 */
2160static void
2161listsetvar(struct strlist *list_set_var, int flags)
2162{
2163 struct strlist *lp = list_set_var;
2164
2165 if (!lp)
2166 return;
2167 INT_OFF;
2168 do {
2169 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002170 lp = lp->next;
2171 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002172 INT_ON;
2173}
2174
2175/*
2176 * Generate a list of variables satisfying the given conditions.
2177 */
2178static char **
2179listvars(int on, int off, char ***end)
2180{
2181 struct var **vpp;
2182 struct var *vp;
2183 char **ep;
2184 int mask;
2185
2186 STARTSTACKSTR(ep);
2187 vpp = vartab;
2188 mask = on | off;
2189 do {
2190 for (vp = *vpp; vp; vp = vp->next) {
2191 if ((vp->flags & mask) == on) {
2192 if (ep == stackstrend())
2193 ep = growstackstr();
2194 *ep++ = (char *) vp->text;
2195 }
2196 }
2197 } while (++vpp < vartab + VTABSIZE);
2198 if (ep == stackstrend())
2199 ep = growstackstr();
2200 if (end)
2201 *end = ep;
2202 *ep++ = NULL;
2203 return grabstackstr(ep);
2204}
2205
2206
2207/* ============ Path search helper
2208 *
2209 * The variable path (passed by reference) should be set to the start
2210 * of the path before the first call; padvance will update
2211 * this value as it proceeds. Successive calls to padvance will return
2212 * the possible path expansions in sequence. If an option (indicated by
2213 * a percent sign) appears in the path entry then the global variable
2214 * pathopt will be set to point to it; otherwise pathopt will be set to
2215 * NULL.
2216 */
2217static const char *pathopt; /* set by padvance */
2218
2219static char *
2220padvance(const char **path, const char *name)
2221{
2222 const char *p;
2223 char *q;
2224 const char *start;
2225 size_t len;
2226
2227 if (*path == NULL)
2228 return NULL;
2229 start = *path;
2230 for (p = start; *p && *p != ':' && *p != '%'; p++);
2231 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2232 while (stackblocksize() < len)
2233 growstackblock();
2234 q = stackblock();
2235 if (p != start) {
2236 memcpy(q, start, p - start);
2237 q += p - start;
2238 *q++ = '/';
2239 }
2240 strcpy(q, name);
2241 pathopt = NULL;
2242 if (*p == '%') {
2243 pathopt = ++p;
2244 while (*p && *p != ':') p++;
2245 }
2246 if (*p == ':')
2247 *path = p + 1;
2248 else
2249 *path = NULL;
2250 return stalloc(len);
2251}
2252
2253
2254/* ============ Prompt */
2255
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002256static smallint doprompt; /* if set, prompt the user */
2257static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002258
2259#if ENABLE_FEATURE_EDITING
2260static line_input_t *line_input_state;
2261static const char *cmdedit_prompt;
2262static void
2263putprompt(const char *s)
2264{
2265 if (ENABLE_ASH_EXPAND_PRMT) {
2266 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002267 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002268 return;
2269 }
2270 cmdedit_prompt = s;
2271}
2272#else
2273static void
2274putprompt(const char *s)
2275{
2276 out2str(s);
2277}
2278#endif
2279
2280#if ENABLE_ASH_EXPAND_PRMT
2281/* expandstr() needs parsing machinery, so it is far away ahead... */
2282static const char *expandstr(const char *ps);
2283#else
2284#define expandstr(s) s
2285#endif
2286
2287static void
2288setprompt(int whichprompt)
2289{
2290 const char *prompt;
2291#if ENABLE_ASH_EXPAND_PRMT
2292 struct stackmark smark;
2293#endif
2294
2295 needprompt = 0;
2296
2297 switch (whichprompt) {
2298 case 1:
2299 prompt = ps1val();
2300 break;
2301 case 2:
2302 prompt = ps2val();
2303 break;
2304 default: /* 0 */
2305 prompt = nullstr;
2306 }
2307#if ENABLE_ASH_EXPAND_PRMT
2308 setstackmark(&smark);
2309 stalloc(stackblocksize());
2310#endif
2311 putprompt(expandstr(prompt));
2312#if ENABLE_ASH_EXPAND_PRMT
2313 popstackmark(&smark);
2314#endif
2315}
2316
2317
2318/* ============ The cd and pwd commands */
2319
2320#define CD_PHYSICAL 1
2321#define CD_PRINT 2
2322
2323static int docd(const char *, int);
2324
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002325static int
2326cdopt(void)
2327{
2328 int flags = 0;
2329 int i, j;
2330
2331 j = 'L';
2332 while ((i = nextopt("LP"))) {
2333 if (i != j) {
2334 flags ^= CD_PHYSICAL;
2335 j = i;
2336 }
2337 }
2338
2339 return flags;
2340}
2341
2342/*
2343 * Update curdir (the name of the current directory) in response to a
2344 * cd command.
2345 */
2346static const char *
2347updatepwd(const char *dir)
2348{
2349 char *new;
2350 char *p;
2351 char *cdcomppath;
2352 const char *lim;
2353
2354 cdcomppath = ststrdup(dir);
2355 STARTSTACKSTR(new);
2356 if (*dir != '/') {
2357 if (curdir == nullstr)
2358 return 0;
2359 new = stack_putstr(curdir, new);
2360 }
2361 new = makestrspace(strlen(dir) + 2, new);
2362 lim = stackblock() + 1;
2363 if (*dir != '/') {
2364 if (new[-1] != '/')
2365 USTPUTC('/', new);
2366 if (new > lim && *lim == '/')
2367 lim++;
2368 } else {
2369 USTPUTC('/', new);
2370 cdcomppath++;
2371 if (dir[1] == '/' && dir[2] != '/') {
2372 USTPUTC('/', new);
2373 cdcomppath++;
2374 lim++;
2375 }
2376 }
2377 p = strtok(cdcomppath, "/");
2378 while (p) {
2379 switch (*p) {
2380 case '.':
2381 if (p[1] == '.' && p[2] == '\0') {
2382 while (new > lim) {
2383 STUNPUTC(new);
2384 if (new[-1] == '/')
2385 break;
2386 }
2387 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002388 }
2389 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390 break;
2391 /* fall through */
2392 default:
2393 new = stack_putstr(p, new);
2394 USTPUTC('/', new);
2395 }
2396 p = strtok(0, "/");
2397 }
2398 if (new > lim)
2399 STUNPUTC(new);
2400 *new = 0;
2401 return stackblock();
2402}
2403
2404/*
2405 * Find out what the current directory is. If we already know the current
2406 * directory, this routine returns immediately.
2407 */
2408static char *
2409getpwd(void)
2410{
Denis Vlasenko01631112007-12-16 17:20:38 +00002411 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412 return dir ? dir : nullstr;
2413}
2414
2415static void
2416setpwd(const char *val, int setold)
2417{
2418 char *oldcur, *dir;
2419
2420 oldcur = dir = curdir;
2421
2422 if (setold) {
2423 setvar("OLDPWD", oldcur, VEXPORT);
2424 }
2425 INT_OFF;
2426 if (physdir != nullstr) {
2427 if (physdir != oldcur)
2428 free(physdir);
2429 physdir = nullstr;
2430 }
2431 if (oldcur == val || !val) {
2432 char *s = getpwd();
2433 physdir = s;
2434 if (!val)
2435 dir = s;
2436 } else
2437 dir = ckstrdup(val);
2438 if (oldcur != dir && oldcur != nullstr) {
2439 free(oldcur);
2440 }
2441 curdir = dir;
2442 INT_ON;
2443 setvar("PWD", dir, VEXPORT);
2444}
2445
2446static void hashcd(void);
2447
2448/*
2449 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2450 * know that the current directory has changed.
2451 */
2452static int
2453docd(const char *dest, int flags)
2454{
2455 const char *dir = 0;
2456 int err;
2457
2458 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2459
2460 INT_OFF;
2461 if (!(flags & CD_PHYSICAL)) {
2462 dir = updatepwd(dest);
2463 if (dir)
2464 dest = dir;
2465 }
2466 err = chdir(dest);
2467 if (err)
2468 goto out;
2469 setpwd(dir, 1);
2470 hashcd();
2471 out:
2472 INT_ON;
2473 return err;
2474}
2475
2476static int
2477cdcmd(int argc, char **argv)
2478{
2479 const char *dest;
2480 const char *path;
2481 const char *p;
2482 char c;
2483 struct stat statb;
2484 int flags;
2485
2486 flags = cdopt();
2487 dest = *argptr;
2488 if (!dest)
2489 dest = bltinlookup(homestr);
2490 else if (LONE_DASH(dest)) {
2491 dest = bltinlookup("OLDPWD");
2492 flags |= CD_PRINT;
2493 }
2494 if (!dest)
2495 dest = nullstr;
2496 if (*dest == '/')
2497 goto step7;
2498 if (*dest == '.') {
2499 c = dest[1];
2500 dotdot:
2501 switch (c) {
2502 case '\0':
2503 case '/':
2504 goto step6;
2505 case '.':
2506 c = dest[2];
2507 if (c != '.')
2508 goto dotdot;
2509 }
2510 }
2511 if (!*dest)
2512 dest = ".";
2513 path = bltinlookup("CDPATH");
2514 if (!path) {
2515 step6:
2516 step7:
2517 p = dest;
2518 goto docd;
2519 }
2520 do {
2521 c = *path;
2522 p = padvance(&path, dest);
2523 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2524 if (c && c != ':')
2525 flags |= CD_PRINT;
2526 docd:
2527 if (!docd(p, flags))
2528 goto out;
2529 break;
2530 }
2531 } while (path);
2532 ash_msg_and_raise_error("can't cd to %s", dest);
2533 /* NOTREACHED */
2534 out:
2535 if (flags & CD_PRINT)
2536 out1fmt(snlfmt, curdir);
2537 return 0;
2538}
2539
2540static int
2541pwdcmd(int argc, char **argv)
2542{
2543 int flags;
2544 const char *dir = curdir;
2545
2546 flags = cdopt();
2547 if (flags) {
2548 if (physdir == nullstr)
2549 setpwd(dir, 0);
2550 dir = physdir;
2551 }
2552 out1fmt(snlfmt, dir);
2553 return 0;
2554}
2555
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002556
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002557/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002558
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002559#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002560#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002561
Eric Andersenc470f442003-07-28 09:56:35 +00002562/* Syntax classes */
2563#define CWORD 0 /* character is nothing special */
2564#define CNL 1 /* newline character */
2565#define CBACK 2 /* a backslash character */
2566#define CSQUOTE 3 /* single quote */
2567#define CDQUOTE 4 /* double quote */
2568#define CENDQUOTE 5 /* a terminating quote */
2569#define CBQUOTE 6 /* backwards single quote */
2570#define CVAR 7 /* a dollar sign */
2571#define CENDVAR 8 /* a '}' character */
2572#define CLP 9 /* a left paren in arithmetic */
2573#define CRP 10 /* a right paren in arithmetic */
2574#define CENDFILE 11 /* end of file */
2575#define CCTL 12 /* like CWORD, except it must be escaped */
2576#define CSPCL 13 /* these terminate a word */
2577#define CIGN 14 /* character should be ignored */
2578
Denis Vlasenko131ae172007-02-18 13:00:19 +00002579#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002580#define SYNBASE 130
2581#define PEOF -130
2582#define PEOA -129
2583#define PEOA_OR_PEOF PEOA
2584#else
2585#define SYNBASE 129
2586#define PEOF -129
2587#define PEOA_OR_PEOF PEOF
2588#endif
2589
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002590/* number syntax index */
2591#define BASESYNTAX 0 /* not in quotes */
2592#define DQSYNTAX 1 /* in double quotes */
2593#define SQSYNTAX 2 /* in single quotes */
2594#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002595#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002596
Denis Vlasenko131ae172007-02-18 13:00:19 +00002597#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002598#define USE_SIT_FUNCTION
2599#endif
2600
Denis Vlasenko131ae172007-02-18 13:00:19 +00002601#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002602static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002603#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002604 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002605#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2607 { CNL, CNL, CNL, CNL }, /* 2, \n */
2608 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2609 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2610 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2611 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2612 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2613 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2614 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2615 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2616 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002617#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002618 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2619 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2620 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002621#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002622};
Eric Andersenc470f442003-07-28 09:56:35 +00002623#else
2624static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002625#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002626 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002627#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002628 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2629 { CNL, CNL, CNL }, /* 2, \n */
2630 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2631 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2632 { CVAR, CVAR, CWORD }, /* 5, $ */
2633 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2634 { CSPCL, CWORD, CWORD }, /* 7, ( */
2635 { CSPCL, CWORD, CWORD }, /* 8, ) */
2636 { CBACK, CBACK, CCTL }, /* 9, \ */
2637 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2638 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002639#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002640 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2641 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2642 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002643#endif
2644};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002645#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002646
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002647#ifdef USE_SIT_FUNCTION
2648
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002649static int
2650SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002651{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002652 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002653#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002654 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002655 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2656 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2657 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2658 11, 3 /* "}~" */
2659 };
2660#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002661 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002662 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2663 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2664 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2665 10, 2 /* "}~" */
2666 };
2667#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002668 const char *s;
2669 int indx;
2670
Eric Andersenc470f442003-07-28 09:56:35 +00002671 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002672 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002673#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002674 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002676 else
2677#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002678#define U_C(c) ((unsigned char)(c))
2679
2680 if ((unsigned char)c >= (unsigned char)(CTLESC)
2681 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2682 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002683 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002684 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002685 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002686 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002687 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002688 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002689 }
2690 return S_I_T[indx][syntax];
2691}
2692
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002693#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002694
Denis Vlasenko131ae172007-02-18 13:00:19 +00002695#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002696#define CSPCL_CIGN_CIGN_CIGN 0
2697#define CSPCL_CWORD_CWORD_CWORD 1
2698#define CNL_CNL_CNL_CNL 2
2699#define CWORD_CCTL_CCTL_CWORD 3
2700#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2701#define CVAR_CVAR_CWORD_CVAR 5
2702#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2703#define CSPCL_CWORD_CWORD_CLP 7
2704#define CSPCL_CWORD_CWORD_CRP 8
2705#define CBACK_CBACK_CCTL_CBACK 9
2706#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2707#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2708#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2709#define CWORD_CWORD_CWORD_CWORD 13
2710#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002711#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002712#define CSPCL_CWORD_CWORD_CWORD 0
2713#define CNL_CNL_CNL_CNL 1
2714#define CWORD_CCTL_CCTL_CWORD 2
2715#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2716#define CVAR_CVAR_CWORD_CVAR 4
2717#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2718#define CSPCL_CWORD_CWORD_CLP 6
2719#define CSPCL_CWORD_CWORD_CRP 7
2720#define CBACK_CBACK_CCTL_CBACK 8
2721#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2722#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2723#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2724#define CWORD_CWORD_CWORD_CWORD 12
2725#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002726#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002727
2728static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002729 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002730 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002731#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002732 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2733#endif
2734 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2736 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2737 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2738 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2739 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2740 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2741 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2742 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002743 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2872 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2873 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2895 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002896 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002897 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2899 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002901 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002902 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2903 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2904 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2905 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2907 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2909 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2921 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2922 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2923 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2924 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2925 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2926 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2954 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2955 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2956 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2959 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2987 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2988 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2989 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002990};
2991
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002992#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2993
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002994#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002995
Eric Andersen2870d962001-07-02 17:27:21 +00002996
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002997/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002998
Denis Vlasenko131ae172007-02-18 13:00:19 +00002999#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003000
3001#define ALIASINUSE 1
3002#define ALIASDEAD 2
3003
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003004struct alias {
3005 struct alias *next;
3006 char *name;
3007 char *val;
3008 int flag;
3009};
3010
Denis Vlasenko01631112007-12-16 17:20:38 +00003011
3012static struct alias **atab; // [ATABSIZE];
3013#define INIT_G_alias() do { \
3014 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3015} while (0)
3016
Eric Andersen2870d962001-07-02 17:27:21 +00003017
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003018static struct alias **
3019__lookupalias(const char *name) {
3020 unsigned int hashval;
3021 struct alias **app;
3022 const char *p;
3023 unsigned int ch;
3024
3025 p = name;
3026
3027 ch = (unsigned char)*p;
3028 hashval = ch << 4;
3029 while (ch) {
3030 hashval += ch;
3031 ch = (unsigned char)*++p;
3032 }
3033 app = &atab[hashval % ATABSIZE];
3034
3035 for (; *app; app = &(*app)->next) {
3036 if (strcmp(name, (*app)->name) == 0) {
3037 break;
3038 }
3039 }
3040
3041 return app;
3042}
3043
3044static struct alias *
3045lookupalias(const char *name, int check)
3046{
3047 struct alias *ap = *__lookupalias(name);
3048
3049 if (check && ap && (ap->flag & ALIASINUSE))
3050 return NULL;
3051 return ap;
3052}
3053
3054static struct alias *
3055freealias(struct alias *ap)
3056{
3057 struct alias *next;
3058
3059 if (ap->flag & ALIASINUSE) {
3060 ap->flag |= ALIASDEAD;
3061 return ap;
3062 }
3063
3064 next = ap->next;
3065 free(ap->name);
3066 free(ap->val);
3067 free(ap);
3068 return next;
3069}
Eric Andersencb57d552001-06-28 07:25:16 +00003070
Eric Andersenc470f442003-07-28 09:56:35 +00003071static void
3072setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003073{
3074 struct alias *ap, **app;
3075
3076 app = __lookupalias(name);
3077 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003078 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003079 if (ap) {
3080 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003081 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003082 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003083 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003084 ap->flag &= ~ALIASDEAD;
3085 } else {
3086 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003087 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003088 ap->name = ckstrdup(name);
3089 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003090 /*ap->flag = 0; - ckzalloc did it */
3091 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003092 *app = ap;
3093 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003094 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003095}
3096
Eric Andersenc470f442003-07-28 09:56:35 +00003097static int
3098unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003099{
Eric Andersencb57d552001-06-28 07:25:16 +00003100 struct alias **app;
3101
3102 app = __lookupalias(name);
3103
3104 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003105 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003106 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003107 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003108 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003109 }
3110
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003111 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003112}
3113
Eric Andersenc470f442003-07-28 09:56:35 +00003114static void
3115rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003116{
Eric Andersencb57d552001-06-28 07:25:16 +00003117 struct alias *ap, **app;
3118 int i;
3119
Denis Vlasenkob012b102007-02-19 22:43:01 +00003120 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003121 for (i = 0; i < ATABSIZE; i++) {
3122 app = &atab[i];
3123 for (ap = *app; ap; ap = *app) {
3124 *app = freealias(*app);
3125 if (ap == *app) {
3126 app = &ap->next;
3127 }
3128 }
3129 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003130 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003131}
3132
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003133static void
3134printalias(const struct alias *ap)
3135{
3136 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3137}
3138
Eric Andersencb57d552001-06-28 07:25:16 +00003139/*
3140 * TODO - sort output
3141 */
Eric Andersenc470f442003-07-28 09:56:35 +00003142static int
3143aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003144{
3145 char *n, *v;
3146 int ret = 0;
3147 struct alias *ap;
3148
3149 if (argc == 1) {
3150 int i;
3151
3152 for (i = 0; i < ATABSIZE; i++)
3153 for (ap = atab[i]; ap; ap = ap->next) {
3154 printalias(ap);
3155 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003156 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003157 }
3158 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003159 v = strchr(n+1, '=');
3160 if (v == NULL) { /* n+1: funny ksh stuff */
3161 ap = *__lookupalias(n);
3162 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003163 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003164 ret = 1;
3165 } else
3166 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003167 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003168 *v++ = '\0';
3169 setalias(n, v);
3170 }
3171 }
3172
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003173 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003174}
3175
Eric Andersenc470f442003-07-28 09:56:35 +00003176static int
3177unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003178{
3179 int i;
3180
3181 while ((i = nextopt("a")) != '\0') {
3182 if (i == 'a') {
3183 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003184 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003185 }
3186 }
3187 for (i = 0; *argptr; argptr++) {
3188 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003189 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003190 i = 1;
3191 }
3192 }
3193
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003194 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003195}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003196
Denis Vlasenko131ae172007-02-18 13:00:19 +00003197#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003198
Eric Andersenc470f442003-07-28 09:56:35 +00003199
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003200/* ============ jobs.c */
3201
3202/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3203#define FORK_FG 0
3204#define FORK_BG 1
3205#define FORK_NOJOB 2
3206
3207/* mode flags for showjob(s) */
3208#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3209#define SHOW_PID 0x04 /* include process pid */
3210#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3211
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003212/*
3213 * A job structure contains information about a job. A job is either a
3214 * single process or a set of processes contained in a pipeline. In the
3215 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3216 * array of pids.
3217 */
3218
3219struct procstat {
3220 pid_t pid; /* process id */
3221 int status; /* last process status from wait() */
3222 char *cmd; /* text of command being run */
3223};
3224
3225struct job {
3226 struct procstat ps0; /* status of process */
3227 struct procstat *ps; /* status or processes when more than one */
3228#if JOBS
3229 int stopstatus; /* status of a stopped job */
3230#endif
3231 uint32_t
3232 nprocs: 16, /* number of processes */
3233 state: 8,
3234#define JOBRUNNING 0 /* at least one proc running */
3235#define JOBSTOPPED 1 /* all procs are stopped */
3236#define JOBDONE 2 /* all procs are completed */
3237#if JOBS
3238 sigint: 1, /* job was killed by SIGINT */
3239 jobctl: 1, /* job running under job control */
3240#endif
3241 waited: 1, /* true if this entry has been waited for */
3242 used: 1, /* true if this entry is in used */
3243 changed: 1; /* true if status has changed */
3244 struct job *prev_job; /* previous job */
3245};
3246
3247static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003248static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003249
3250static struct job *makejob(union node *, int);
3251static int forkshell(struct job *, union node *, int);
3252static int waitforjob(struct job *);
3253
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003254#if !JOBS
3255enum { jobctl = 0 };
3256#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003257#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003258static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003259static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003260#endif
3261
3262/*
3263 * Set the signal handler for the specified signal. The routine figures
3264 * out what it should be set to.
3265 */
3266static void
3267setsignal(int signo)
3268{
3269 int action;
3270 char *t, tsig;
3271 struct sigaction act;
3272
3273 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003274 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003275 if (t == NULL)
3276 action = S_DFL;
3277 else if (*t != '\0')
3278 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003279 if (rootshell && action == S_DFL) {
3280 switch (signo) {
3281 case SIGINT:
3282 if (iflag || minusc || sflag == 0)
3283 action = S_CATCH;
3284 break;
3285 case SIGQUIT:
3286#if DEBUG
3287 if (debug)
3288 break;
3289#endif
3290 /* FALLTHROUGH */
3291 case SIGTERM:
3292 if (iflag)
3293 action = S_IGN;
3294 break;
3295#if JOBS
3296 case SIGTSTP:
3297 case SIGTTOU:
3298 if (mflag)
3299 action = S_IGN;
3300 break;
3301#endif
3302 }
3303 }
3304
3305 t = &sigmode[signo - 1];
3306 tsig = *t;
3307 if (tsig == 0) {
3308 /*
3309 * current setting unknown
3310 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003311 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003312 /*
3313 * Pretend it worked; maybe we should give a warning
3314 * here, but other shells don't. We don't alter
3315 * sigmode, so that we retry every time.
3316 */
3317 return;
3318 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003319 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003321 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003322 if (mflag
3323 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3324 ) {
3325 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003326 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327 }
3328 }
3329 if (tsig == S_HARD_IGN || tsig == action)
3330 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003331 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332 switch (action) {
3333 case S_CATCH:
3334 act.sa_handler = onsig;
3335 break;
3336 case S_IGN:
3337 act.sa_handler = SIG_IGN;
3338 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339 }
3340 *t = action;
3341 act.sa_flags = 0;
3342 sigfillset(&act.sa_mask);
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003343 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003344}
3345
3346/* mode flags for set_curjob */
3347#define CUR_DELETE 2
3348#define CUR_RUNNING 1
3349#define CUR_STOPPED 0
3350
3351/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003352#define DOWAIT_NONBLOCK WNOHANG
3353#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003354
3355#if JOBS
3356/* pgrp of shell on invocation */
3357static int initialpgrp;
3358static int ttyfd = -1;
3359#endif
3360/* array of jobs */
3361static struct job *jobtab;
3362/* size of array */
3363static unsigned njobs;
3364/* current job */
3365static struct job *curjob;
3366/* number of presumed living untracked jobs */
3367static int jobless;
3368
3369static void
3370set_curjob(struct job *jp, unsigned mode)
3371{
3372 struct job *jp1;
3373 struct job **jpp, **curp;
3374
3375 /* first remove from list */
3376 jpp = curp = &curjob;
3377 do {
3378 jp1 = *jpp;
3379 if (jp1 == jp)
3380 break;
3381 jpp = &jp1->prev_job;
3382 } while (1);
3383 *jpp = jp1->prev_job;
3384
3385 /* Then re-insert in correct position */
3386 jpp = curp;
3387 switch (mode) {
3388 default:
3389#if DEBUG
3390 abort();
3391#endif
3392 case CUR_DELETE:
3393 /* job being deleted */
3394 break;
3395 case CUR_RUNNING:
3396 /* newly created job or backgrounded job,
3397 put after all stopped jobs. */
3398 do {
3399 jp1 = *jpp;
3400#if JOBS
3401 if (!jp1 || jp1->state != JOBSTOPPED)
3402#endif
3403 break;
3404 jpp = &jp1->prev_job;
3405 } while (1);
3406 /* FALLTHROUGH */
3407#if JOBS
3408 case CUR_STOPPED:
3409#endif
3410 /* newly stopped job - becomes curjob */
3411 jp->prev_job = *jpp;
3412 *jpp = jp;
3413 break;
3414 }
3415}
3416
3417#if JOBS || DEBUG
3418static int
3419jobno(const struct job *jp)
3420{
3421 return jp - jobtab + 1;
3422}
3423#endif
3424
3425/*
3426 * Convert a job name to a job structure.
3427 */
3428static struct job *
3429getjob(const char *name, int getctl)
3430{
3431 struct job *jp;
3432 struct job *found;
3433 const char *err_msg = "No such job: %s";
3434 unsigned num;
3435 int c;
3436 const char *p;
3437 char *(*match)(const char *, const char *);
3438
3439 jp = curjob;
3440 p = name;
3441 if (!p)
3442 goto currentjob;
3443
3444 if (*p != '%')
3445 goto err;
3446
3447 c = *++p;
3448 if (!c)
3449 goto currentjob;
3450
3451 if (!p[1]) {
3452 if (c == '+' || c == '%') {
3453 currentjob:
3454 err_msg = "No current job";
3455 goto check;
3456 }
3457 if (c == '-') {
3458 if (jp)
3459 jp = jp->prev_job;
3460 err_msg = "No previous job";
3461 check:
3462 if (!jp)
3463 goto err;
3464 goto gotit;
3465 }
3466 }
3467
3468 if (is_number(p)) {
3469 num = atoi(p);
3470 if (num < njobs) {
3471 jp = jobtab + num - 1;
3472 if (jp->used)
3473 goto gotit;
3474 goto err;
3475 }
3476 }
3477
3478 match = prefix;
3479 if (*p == '?') {
3480 match = strstr;
3481 p++;
3482 }
3483
3484 found = 0;
3485 while (1) {
3486 if (!jp)
3487 goto err;
3488 if (match(jp->ps[0].cmd, p)) {
3489 if (found)
3490 goto err;
3491 found = jp;
3492 err_msg = "%s: ambiguous";
3493 }
3494 jp = jp->prev_job;
3495 }
3496
3497 gotit:
3498#if JOBS
3499 err_msg = "job %s not created under job control";
3500 if (getctl && jp->jobctl == 0)
3501 goto err;
3502#endif
3503 return jp;
3504 err:
3505 ash_msg_and_raise_error(err_msg, name);
3506}
3507
3508/*
3509 * Mark a job structure as unused.
3510 */
3511static void
3512freejob(struct job *jp)
3513{
3514 struct procstat *ps;
3515 int i;
3516
3517 INT_OFF;
3518 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3519 if (ps->cmd != nullstr)
3520 free(ps->cmd);
3521 }
3522 if (jp->ps != &jp->ps0)
3523 free(jp->ps);
3524 jp->used = 0;
3525 set_curjob(jp, CUR_DELETE);
3526 INT_ON;
3527}
3528
3529#if JOBS
3530static void
3531xtcsetpgrp(int fd, pid_t pgrp)
3532{
3533 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003534 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003535}
3536
3537/*
3538 * Turn job control on and off.
3539 *
3540 * Note: This code assumes that the third arg to ioctl is a character
3541 * pointer, which is true on Berkeley systems but not System V. Since
3542 * System V doesn't have job control yet, this isn't a problem now.
3543 *
3544 * Called with interrupts off.
3545 */
3546static void
3547setjobctl(int on)
3548{
3549 int fd;
3550 int pgrp;
3551
3552 if (on == jobctl || rootshell == 0)
3553 return;
3554 if (on) {
3555 int ofd;
3556 ofd = fd = open(_PATH_TTY, O_RDWR);
3557 if (fd < 0) {
3558 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3559 * That sometimes helps to acquire controlling tty.
3560 * Obviously, a workaround for bugs when someone
3561 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003562 fd = 2;
3563 while (!isatty(fd))
3564 if (--fd < 0)
3565 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003566 }
3567 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003568 if (ofd >= 0)
3569 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003570 if (fd < 0)
3571 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003572 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003573 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003574 do { /* while we are in the background */
3575 pgrp = tcgetpgrp(fd);
3576 if (pgrp < 0) {
3577 out:
3578 ash_msg("can't access tty; job control turned off");
3579 mflag = on = 0;
3580 goto close;
3581 }
3582 if (pgrp == getpgrp())
3583 break;
3584 killpg(0, SIGTTIN);
3585 } while (1);
3586 initialpgrp = pgrp;
3587
3588 setsignal(SIGTSTP);
3589 setsignal(SIGTTOU);
3590 setsignal(SIGTTIN);
3591 pgrp = rootpid;
3592 setpgid(0, pgrp);
3593 xtcsetpgrp(fd, pgrp);
3594 } else {
3595 /* turning job control off */
3596 fd = ttyfd;
3597 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003598 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003599 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003600 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003601 setpgid(0, pgrp);
3602 setsignal(SIGTSTP);
3603 setsignal(SIGTTOU);
3604 setsignal(SIGTTIN);
3605 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003606 if (fd >= 0)
3607 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003608 fd = -1;
3609 }
3610 ttyfd = fd;
3611 jobctl = on;
3612}
3613
3614static int
3615killcmd(int argc, char **argv)
3616{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003617 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3618 int i = 1;
3619 do {
3620 if (argv[i][0] == '%') {
3621 struct job *jp = getjob(argv[i], 0);
3622 unsigned pid = jp->ps[0].pid;
3623 /* Enough space for ' -NNN<nul>' */
3624 argv[i] = alloca(sizeof(int)*3 + 3);
3625 /* kill_main has matching code to expect
3626 * leading space. Needed to not confuse
3627 * negative pids with "kill -SIGNAL_NO" syntax */
3628 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003629 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003630 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003631 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003632 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003633}
3634
3635static void
3636showpipe(struct job *jp, FILE *out)
3637{
3638 struct procstat *sp;
3639 struct procstat *spend;
3640
3641 spend = jp->ps + jp->nprocs;
3642 for (sp = jp->ps + 1; sp < spend; sp++)
3643 fprintf(out, " | %s", sp->cmd);
3644 outcslow('\n', out);
3645 flush_stdout_stderr();
3646}
3647
3648
3649static int
3650restartjob(struct job *jp, int mode)
3651{
3652 struct procstat *ps;
3653 int i;
3654 int status;
3655 pid_t pgid;
3656
3657 INT_OFF;
3658 if (jp->state == JOBDONE)
3659 goto out;
3660 jp->state = JOBRUNNING;
3661 pgid = jp->ps->pid;
3662 if (mode == FORK_FG)
3663 xtcsetpgrp(ttyfd, pgid);
3664 killpg(pgid, SIGCONT);
3665 ps = jp->ps;
3666 i = jp->nprocs;
3667 do {
3668 if (WIFSTOPPED(ps->status)) {
3669 ps->status = -1;
3670 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003671 ps++;
3672 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003673 out:
3674 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3675 INT_ON;
3676 return status;
3677}
3678
3679static int
3680fg_bgcmd(int argc, char **argv)
3681{
3682 struct job *jp;
3683 FILE *out;
3684 int mode;
3685 int retval;
3686
3687 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3688 nextopt(nullstr);
3689 argv = argptr;
3690 out = stdout;
3691 do {
3692 jp = getjob(*argv, 1);
3693 if (mode == FORK_BG) {
3694 set_curjob(jp, CUR_RUNNING);
3695 fprintf(out, "[%d] ", jobno(jp));
3696 }
3697 outstr(jp->ps->cmd, out);
3698 showpipe(jp, out);
3699 retval = restartjob(jp, mode);
3700 } while (*argv && *++argv);
3701 return retval;
3702}
3703#endif
3704
3705static int
3706sprint_status(char *s, int status, int sigonly)
3707{
3708 int col;
3709 int st;
3710
3711 col = 0;
3712 if (!WIFEXITED(status)) {
3713#if JOBS
3714 if (WIFSTOPPED(status))
3715 st = WSTOPSIG(status);
3716 else
3717#endif
3718 st = WTERMSIG(status);
3719 if (sigonly) {
3720 if (st == SIGINT || st == SIGPIPE)
3721 goto out;
3722#if JOBS
3723 if (WIFSTOPPED(status))
3724 goto out;
3725#endif
3726 }
3727 st &= 0x7f;
3728 col = fmtstr(s, 32, strsignal(st));
3729 if (WCOREDUMP(status)) {
3730 col += fmtstr(s + col, 16, " (core dumped)");
3731 }
3732 } else if (!sigonly) {
3733 st = WEXITSTATUS(status);
3734 if (st)
3735 col = fmtstr(s, 16, "Done(%d)", st);
3736 else
3737 col = fmtstr(s, 16, "Done");
3738 }
3739 out:
3740 return col;
3741}
3742
3743/*
3744 * Do a wait system call. If job control is compiled in, we accept
3745 * stopped processes. If block is zero, we return a value of zero
3746 * rather than blocking.
3747 *
3748 * System V doesn't have a non-blocking wait system call. It does
3749 * have a SIGCLD signal that is sent to a process when one of it's
3750 * children dies. The obvious way to use SIGCLD would be to install
3751 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3752 * was received, and have waitproc bump another counter when it got
3753 * the status of a process. Waitproc would then know that a wait
3754 * system call would not block if the two counters were different.
3755 * This approach doesn't work because if a process has children that
3756 * have not been waited for, System V will send it a SIGCLD when it
3757 * installs a signal handler for SIGCLD. What this means is that when
3758 * a child exits, the shell will be sent SIGCLD signals continuously
3759 * until is runs out of stack space, unless it does a wait call before
3760 * restoring the signal handler. The code below takes advantage of
3761 * this (mis)feature by installing a signal handler for SIGCLD and
3762 * then checking to see whether it was called. If there are any
3763 * children to be waited for, it will be.
3764 *
3765 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3766 * waits at all. In this case, the user will not be informed when
3767 * a background process until the next time she runs a real program
3768 * (as opposed to running a builtin command or just typing return),
3769 * and the jobs command may give out of date information.
3770 */
3771static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003772waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003773{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774#if JOBS
3775 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003776 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003778 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3779 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003780}
3781
3782/*
3783 * Wait for a process to terminate.
3784 */
3785static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003786dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003787{
3788 int pid;
3789 int status;
3790 struct job *jp;
3791 struct job *thisjob;
3792 int state;
3793
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003794 TRACE(("dowait(%d) called\n", wait_flags));
3795 pid = waitproc(wait_flags, &status);
3796 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003797 if (pid <= 0) {
3798 /* If we were doing blocking wait and (probably) got EINTR,
3799 * check for pending sigs received while waiting.
3800 * (NB: can be moved into callers if needed) */
3801 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3802 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003803 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003804 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003805 INT_OFF;
3806 thisjob = NULL;
3807 for (jp = curjob; jp; jp = jp->prev_job) {
3808 struct procstat *sp;
3809 struct procstat *spend;
3810 if (jp->state == JOBDONE)
3811 continue;
3812 state = JOBDONE;
3813 spend = jp->ps + jp->nprocs;
3814 sp = jp->ps;
3815 do {
3816 if (sp->pid == pid) {
3817 TRACE(("Job %d: changing status of proc %d "
3818 "from 0x%x to 0x%x\n",
3819 jobno(jp), pid, sp->status, status));
3820 sp->status = status;
3821 thisjob = jp;
3822 }
3823 if (sp->status == -1)
3824 state = JOBRUNNING;
3825#if JOBS
3826 if (state == JOBRUNNING)
3827 continue;
3828 if (WIFSTOPPED(sp->status)) {
3829 jp->stopstatus = sp->status;
3830 state = JOBSTOPPED;
3831 }
3832#endif
3833 } while (++sp < spend);
3834 if (thisjob)
3835 goto gotjob;
3836 }
3837#if JOBS
3838 if (!WIFSTOPPED(status))
3839#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 jobless--;
3841 goto out;
3842
3843 gotjob:
3844 if (state != JOBRUNNING) {
3845 thisjob->changed = 1;
3846
3847 if (thisjob->state != state) {
3848 TRACE(("Job %d: changing state from %d to %d\n",
3849 jobno(thisjob), thisjob->state, state));
3850 thisjob->state = state;
3851#if JOBS
3852 if (state == JOBSTOPPED) {
3853 set_curjob(thisjob, CUR_STOPPED);
3854 }
3855#endif
3856 }
3857 }
3858
3859 out:
3860 INT_ON;
3861
3862 if (thisjob && thisjob == job) {
3863 char s[48 + 1];
3864 int len;
3865
3866 len = sprint_status(s, status, 1);
3867 if (len) {
3868 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003869 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003870 out2str(s);
3871 }
3872 }
3873 return pid;
3874}
3875
3876#if JOBS
3877static void
3878showjob(FILE *out, struct job *jp, int mode)
3879{
3880 struct procstat *ps;
3881 struct procstat *psend;
3882 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003883 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884 char s[80];
3885
3886 ps = jp->ps;
3887
3888 if (mode & SHOW_PGID) {
3889 /* just output process (group) id of pipeline */
3890 fprintf(out, "%d\n", ps->pid);
3891 return;
3892 }
3893
3894 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003895 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896
3897 if (jp == curjob)
3898 s[col - 2] = '+';
3899 else if (curjob && jp == curjob->prev_job)
3900 s[col - 2] = '-';
3901
3902 if (mode & SHOW_PID)
3903 col += fmtstr(s + col, 16, "%d ", ps->pid);
3904
3905 psend = ps + jp->nprocs;
3906
3907 if (jp->state == JOBRUNNING) {
3908 strcpy(s + col, "Running");
3909 col += sizeof("Running") - 1;
3910 } else {
3911 int status = psend[-1].status;
3912 if (jp->state == JOBSTOPPED)
3913 status = jp->stopstatus;
3914 col += sprint_status(s + col, status, 0);
3915 }
3916
3917 goto start;
3918
3919 do {
3920 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003921 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003922 start:
3923 fprintf(out, "%s%*c%s",
3924 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3925 );
3926 if (!(mode & SHOW_PID)) {
3927 showpipe(jp, out);
3928 break;
3929 }
3930 if (++ps == psend) {
3931 outcslow('\n', out);
3932 break;
3933 }
3934 } while (1);
3935
3936 jp->changed = 0;
3937
3938 if (jp->state == JOBDONE) {
3939 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3940 freejob(jp);
3941 }
3942}
3943
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944/*
3945 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3946 * statuses have changed since the last call to showjobs.
3947 */
3948static void
3949showjobs(FILE *out, int mode)
3950{
3951 struct job *jp;
3952
3953 TRACE(("showjobs(%x) called\n", mode));
3954
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003955 /* If not even one job changed, there is nothing to do */
3956 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957 continue;
3958
3959 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003960 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003961 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003962 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003963 }
3964}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003965
3966static int
3967jobscmd(int argc, char **argv)
3968{
3969 int mode, m;
3970
3971 mode = 0;
3972 while ((m = nextopt("lp"))) {
3973 if (m == 'l')
3974 mode = SHOW_PID;
3975 else
3976 mode = SHOW_PGID;
3977 }
3978
3979 argv = argptr;
3980 if (*argv) {
3981 do
3982 showjob(stdout, getjob(*argv,0), mode);
3983 while (*++argv);
3984 } else
3985 showjobs(stdout, mode);
3986
3987 return 0;
3988}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003989#endif /* JOBS */
3990
3991static int
3992getstatus(struct job *job)
3993{
3994 int status;
3995 int retval;
3996
3997 status = job->ps[job->nprocs - 1].status;
3998 retval = WEXITSTATUS(status);
3999 if (!WIFEXITED(status)) {
4000#if JOBS
4001 retval = WSTOPSIG(status);
4002 if (!WIFSTOPPED(status))
4003#endif
4004 {
4005 /* XXX: limits number of signals */
4006 retval = WTERMSIG(status);
4007#if JOBS
4008 if (retval == SIGINT)
4009 job->sigint = 1;
4010#endif
4011 }
4012 retval += 128;
4013 }
4014 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4015 jobno(job), job->nprocs, status, retval));
4016 return retval;
4017}
4018
4019static int
4020waitcmd(int argc, char **argv)
4021{
4022 struct job *job;
4023 int retval;
4024 struct job *jp;
4025
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004026// exsig++;
4027// xbarrier();
4028 if (pendingsig)
4029 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030
4031 nextopt(nullstr);
4032 retval = 0;
4033
4034 argv = argptr;
4035 if (!*argv) {
4036 /* wait for all jobs */
4037 for (;;) {
4038 jp = curjob;
4039 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004040 if (!jp) /* no running procs */
4041 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004042 if (jp->state == JOBRUNNING)
4043 break;
4044 jp->waited = 1;
4045 jp = jp->prev_job;
4046 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004047 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004048 }
4049 }
4050
4051 retval = 127;
4052 do {
4053 if (**argv != '%') {
4054 pid_t pid = number(*argv);
4055 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004056 while (1) {
4057 if (!job)
4058 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 if (job->ps[job->nprocs - 1].pid == pid)
4060 break;
4061 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004062 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 } else
4064 job = getjob(*argv, 0);
4065 /* loop until process terminated or stopped */
4066 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004067 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 job->waited = 1;
4069 retval = getstatus(job);
4070 repeat:
4071 ;
4072 } while (*++argv);
4073
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004074 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075 return retval;
4076}
4077
4078static struct job *
4079growjobtab(void)
4080{
4081 size_t len;
4082 ptrdiff_t offset;
4083 struct job *jp, *jq;
4084
4085 len = njobs * sizeof(*jp);
4086 jq = jobtab;
4087 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4088
4089 offset = (char *)jp - (char *)jq;
4090 if (offset) {
4091 /* Relocate pointers */
4092 size_t l = len;
4093
4094 jq = (struct job *)((char *)jq + l);
4095 while (l) {
4096 l -= sizeof(*jp);
4097 jq--;
4098#define joff(p) ((struct job *)((char *)(p) + l))
4099#define jmove(p) (p) = (void *)((char *)(p) + offset)
4100 if (joff(jp)->ps == &jq->ps0)
4101 jmove(joff(jp)->ps);
4102 if (joff(jp)->prev_job)
4103 jmove(joff(jp)->prev_job);
4104 }
4105 if (curjob)
4106 jmove(curjob);
4107#undef joff
4108#undef jmove
4109 }
4110
4111 njobs += 4;
4112 jobtab = jp;
4113 jp = (struct job *)((char *)jp + len);
4114 jq = jp + 3;
4115 do {
4116 jq->used = 0;
4117 } while (--jq >= jp);
4118 return jp;
4119}
4120
4121/*
4122 * Return a new job structure.
4123 * Called with interrupts off.
4124 */
4125static struct job *
4126makejob(union node *node, int nprocs)
4127{
4128 int i;
4129 struct job *jp;
4130
4131 for (i = njobs, jp = jobtab; ; jp++) {
4132 if (--i < 0) {
4133 jp = growjobtab();
4134 break;
4135 }
4136 if (jp->used == 0)
4137 break;
4138 if (jp->state != JOBDONE || !jp->waited)
4139 continue;
4140#if JOBS
4141 if (jobctl)
4142 continue;
4143#endif
4144 freejob(jp);
4145 break;
4146 }
4147 memset(jp, 0, sizeof(*jp));
4148#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004149 /* jp->jobctl is a bitfield.
4150 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004151 if (jobctl)
4152 jp->jobctl = 1;
4153#endif
4154 jp->prev_job = curjob;
4155 curjob = jp;
4156 jp->used = 1;
4157 jp->ps = &jp->ps0;
4158 if (nprocs > 1) {
4159 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4160 }
4161 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4162 jobno(jp)));
4163 return jp;
4164}
4165
4166#if JOBS
4167/*
4168 * Return a string identifying a command (to be printed by the
4169 * jobs command).
4170 */
4171static char *cmdnextc;
4172
4173static void
4174cmdputs(const char *s)
4175{
4176 const char *p, *str;
4177 char c, cc[2] = " ";
4178 char *nextc;
4179 int subtype = 0;
4180 int quoted = 0;
4181 static const char vstype[VSTYPE + 1][4] = {
4182 "", "}", "-", "+", "?", "=",
4183 "%", "%%", "#", "##"
4184 };
4185
4186 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4187 p = s;
4188 while ((c = *p++) != 0) {
4189 str = 0;
4190 switch (c) {
4191 case CTLESC:
4192 c = *p++;
4193 break;
4194 case CTLVAR:
4195 subtype = *p++;
4196 if ((subtype & VSTYPE) == VSLENGTH)
4197 str = "${#";
4198 else
4199 str = "${";
4200 if (!(subtype & VSQUOTE) == !(quoted & 1))
4201 goto dostr;
4202 quoted ^= 1;
4203 c = '"';
4204 break;
4205 case CTLENDVAR:
4206 str = "\"}" + !(quoted & 1);
4207 quoted >>= 1;
4208 subtype = 0;
4209 goto dostr;
4210 case CTLBACKQ:
4211 str = "$(...)";
4212 goto dostr;
4213 case CTLBACKQ+CTLQUOTE:
4214 str = "\"$(...)\"";
4215 goto dostr;
4216#if ENABLE_ASH_MATH_SUPPORT
4217 case CTLARI:
4218 str = "$((";
4219 goto dostr;
4220 case CTLENDARI:
4221 str = "))";
4222 goto dostr;
4223#endif
4224 case CTLQUOTEMARK:
4225 quoted ^= 1;
4226 c = '"';
4227 break;
4228 case '=':
4229 if (subtype == 0)
4230 break;
4231 if ((subtype & VSTYPE) != VSNORMAL)
4232 quoted <<= 1;
4233 str = vstype[subtype & VSTYPE];
4234 if (subtype & VSNUL)
4235 c = ':';
4236 else
4237 goto checkstr;
4238 break;
4239 case '\'':
4240 case '\\':
4241 case '"':
4242 case '$':
4243 /* These can only happen inside quotes */
4244 cc[0] = c;
4245 str = cc;
4246 c = '\\';
4247 break;
4248 default:
4249 break;
4250 }
4251 USTPUTC(c, nextc);
4252 checkstr:
4253 if (!str)
4254 continue;
4255 dostr:
4256 while ((c = *str++)) {
4257 USTPUTC(c, nextc);
4258 }
4259 }
4260 if (quoted & 1) {
4261 USTPUTC('"', nextc);
4262 }
4263 *nextc = 0;
4264 cmdnextc = nextc;
4265}
4266
4267/* cmdtxt() and cmdlist() call each other */
4268static void cmdtxt(union node *n);
4269
4270static void
4271cmdlist(union node *np, int sep)
4272{
4273 for (; np; np = np->narg.next) {
4274 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004275 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004276 cmdtxt(np);
4277 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004278 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004279 }
4280}
4281
4282static void
4283cmdtxt(union node *n)
4284{
4285 union node *np;
4286 struct nodelist *lp;
4287 const char *p;
4288 char s[2];
4289
4290 if (!n)
4291 return;
4292 switch (n->type) {
4293 default:
4294#if DEBUG
4295 abort();
4296#endif
4297 case NPIPE:
4298 lp = n->npipe.cmdlist;
4299 for (;;) {
4300 cmdtxt(lp->n);
4301 lp = lp->next;
4302 if (!lp)
4303 break;
4304 cmdputs(" | ");
4305 }
4306 break;
4307 case NSEMI:
4308 p = "; ";
4309 goto binop;
4310 case NAND:
4311 p = " && ";
4312 goto binop;
4313 case NOR:
4314 p = " || ";
4315 binop:
4316 cmdtxt(n->nbinary.ch1);
4317 cmdputs(p);
4318 n = n->nbinary.ch2;
4319 goto donode;
4320 case NREDIR:
4321 case NBACKGND:
4322 n = n->nredir.n;
4323 goto donode;
4324 case NNOT:
4325 cmdputs("!");
4326 n = n->nnot.com;
4327 donode:
4328 cmdtxt(n);
4329 break;
4330 case NIF:
4331 cmdputs("if ");
4332 cmdtxt(n->nif.test);
4333 cmdputs("; then ");
4334 n = n->nif.ifpart;
4335 if (n->nif.elsepart) {
4336 cmdtxt(n);
4337 cmdputs("; else ");
4338 n = n->nif.elsepart;
4339 }
4340 p = "; fi";
4341 goto dotail;
4342 case NSUBSHELL:
4343 cmdputs("(");
4344 n = n->nredir.n;
4345 p = ")";
4346 goto dotail;
4347 case NWHILE:
4348 p = "while ";
4349 goto until;
4350 case NUNTIL:
4351 p = "until ";
4352 until:
4353 cmdputs(p);
4354 cmdtxt(n->nbinary.ch1);
4355 n = n->nbinary.ch2;
4356 p = "; done";
4357 dodo:
4358 cmdputs("; do ");
4359 dotail:
4360 cmdtxt(n);
4361 goto dotail2;
4362 case NFOR:
4363 cmdputs("for ");
4364 cmdputs(n->nfor.var);
4365 cmdputs(" in ");
4366 cmdlist(n->nfor.args, 1);
4367 n = n->nfor.body;
4368 p = "; done";
4369 goto dodo;
4370 case NDEFUN:
4371 cmdputs(n->narg.text);
4372 p = "() { ... }";
4373 goto dotail2;
4374 case NCMD:
4375 cmdlist(n->ncmd.args, 1);
4376 cmdlist(n->ncmd.redirect, 0);
4377 break;
4378 case NARG:
4379 p = n->narg.text;
4380 dotail2:
4381 cmdputs(p);
4382 break;
4383 case NHERE:
4384 case NXHERE:
4385 p = "<<...";
4386 goto dotail2;
4387 case NCASE:
4388 cmdputs("case ");
4389 cmdputs(n->ncase.expr->narg.text);
4390 cmdputs(" in ");
4391 for (np = n->ncase.cases; np; np = np->nclist.next) {
4392 cmdtxt(np->nclist.pattern);
4393 cmdputs(") ");
4394 cmdtxt(np->nclist.body);
4395 cmdputs(";; ");
4396 }
4397 p = "esac";
4398 goto dotail2;
4399 case NTO:
4400 p = ">";
4401 goto redir;
4402 case NCLOBBER:
4403 p = ">|";
4404 goto redir;
4405 case NAPPEND:
4406 p = ">>";
4407 goto redir;
4408 case NTOFD:
4409 p = ">&";
4410 goto redir;
4411 case NFROM:
4412 p = "<";
4413 goto redir;
4414 case NFROMFD:
4415 p = "<&";
4416 goto redir;
4417 case NFROMTO:
4418 p = "<>";
4419 redir:
4420 s[0] = n->nfile.fd + '0';
4421 s[1] = '\0';
4422 cmdputs(s);
4423 cmdputs(p);
4424 if (n->type == NTOFD || n->type == NFROMFD) {
4425 s[0] = n->ndup.dupfd + '0';
4426 p = s;
4427 goto dotail2;
4428 }
4429 n = n->nfile.fname;
4430 goto donode;
4431 }
4432}
4433
4434static char *
4435commandtext(union node *n)
4436{
4437 char *name;
4438
4439 STARTSTACKSTR(cmdnextc);
4440 cmdtxt(n);
4441 name = stackblock();
4442 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4443 name, cmdnextc, cmdnextc));
4444 return ckstrdup(name);
4445}
4446#endif /* JOBS */
4447
4448/*
4449 * Fork off a subshell. If we are doing job control, give the subshell its
4450 * own process group. Jp is a job structure that the job is to be added to.
4451 * N is the command that will be evaluated by the child. Both jp and n may
4452 * be NULL. The mode parameter can be one of the following:
4453 * FORK_FG - Fork off a foreground process.
4454 * FORK_BG - Fork off a background process.
4455 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4456 * process group even if job control is on.
4457 *
4458 * When job control is turned off, background processes have their standard
4459 * input redirected to /dev/null (except for the second and later processes
4460 * in a pipeline).
4461 *
4462 * Called with interrupts off.
4463 */
4464/*
4465 * Clear traps on a fork.
4466 */
4467static void
4468clear_traps(void)
4469{
4470 char **tp;
4471
4472 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004473 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004474 INT_OFF;
4475 free(*tp);
4476 *tp = NULL;
4477 if (tp != &trap[0])
4478 setsignal(tp - trap);
4479 INT_ON;
4480 }
4481 }
4482}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004483
4484/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004486
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004487/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488static void
4489forkchild(struct job *jp, union node *n, int mode)
4490{
4491 int oldlvl;
4492
4493 TRACE(("Child shell %d\n", getpid()));
4494 oldlvl = shlvl;
4495 shlvl++;
4496
4497 closescript();
4498 clear_traps();
4499#if JOBS
4500 /* do job control only in root shell */
4501 jobctl = 0;
4502 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4503 pid_t pgrp;
4504
4505 if (jp->nprocs == 0)
4506 pgrp = getpid();
4507 else
4508 pgrp = jp->ps[0].pid;
4509 /* This can fail because we are doing it in the parent also */
4510 (void)setpgid(0, pgrp);
4511 if (mode == FORK_FG)
4512 xtcsetpgrp(ttyfd, pgrp);
4513 setsignal(SIGTSTP);
4514 setsignal(SIGTTOU);
4515 } else
4516#endif
4517 if (mode == FORK_BG) {
4518 ignoresig(SIGINT);
4519 ignoresig(SIGQUIT);
4520 if (jp->nprocs == 0) {
4521 close(0);
4522 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004523 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004524 }
4525 }
4526 if (!oldlvl && iflag) {
4527 setsignal(SIGINT);
4528 setsignal(SIGQUIT);
4529 setsignal(SIGTERM);
4530 }
4531 for (jp = curjob; jp; jp = jp->prev_job)
4532 freejob(jp);
4533 jobless = 0;
4534}
4535
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004536/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537static void
4538forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4539{
4540 TRACE(("In parent shell: child = %d\n", pid));
4541 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004542 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4543 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 jobless++;
4545 return;
4546 }
4547#if JOBS
4548 if (mode != FORK_NOJOB && jp->jobctl) {
4549 int pgrp;
4550
4551 if (jp->nprocs == 0)
4552 pgrp = pid;
4553 else
4554 pgrp = jp->ps[0].pid;
4555 /* This can fail because we are doing it in the child also */
4556 setpgid(pid, pgrp);
4557 }
4558#endif
4559 if (mode == FORK_BG) {
4560 backgndpid = pid; /* set $! */
4561 set_curjob(jp, CUR_RUNNING);
4562 }
4563 if (jp) {
4564 struct procstat *ps = &jp->ps[jp->nprocs++];
4565 ps->pid = pid;
4566 ps->status = -1;
4567 ps->cmd = nullstr;
4568#if JOBS
4569 if (jobctl && n)
4570 ps->cmd = commandtext(n);
4571#endif
4572 }
4573}
4574
4575static int
4576forkshell(struct job *jp, union node *n, int mode)
4577{
4578 int pid;
4579
4580 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4581 pid = fork();
4582 if (pid < 0) {
4583 TRACE(("Fork failed, errno=%d", errno));
4584 if (jp)
4585 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004586 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004587 }
4588 if (pid == 0)
4589 forkchild(jp, n, mode);
4590 else
4591 forkparent(jp, n, mode, pid);
4592 return pid;
4593}
4594
4595/*
4596 * Wait for job to finish.
4597 *
4598 * Under job control we have the problem that while a child process is
4599 * running interrupts generated by the user are sent to the child but not
4600 * to the shell. This means that an infinite loop started by an inter-
4601 * active user may be hard to kill. With job control turned off, an
4602 * interactive user may place an interactive program inside a loop. If
4603 * the interactive program catches interrupts, the user doesn't want
4604 * these interrupts to also abort the loop. The approach we take here
4605 * is to have the shell ignore interrupt signals while waiting for a
4606 * foreground process to terminate, and then send itself an interrupt
4607 * signal if the child process was terminated by an interrupt signal.
4608 * Unfortunately, some programs want to do a bit of cleanup and then
4609 * exit on interrupt; unless these processes terminate themselves by
4610 * sending a signal to themselves (instead of calling exit) they will
4611 * confuse this approach.
4612 *
4613 * Called with interrupts off.
4614 */
4615static int
4616waitforjob(struct job *jp)
4617{
4618 int st;
4619
4620 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4621 while (jp->state == JOBRUNNING) {
4622 dowait(DOWAIT_BLOCK, jp);
4623 }
4624 st = getstatus(jp);
4625#if JOBS
4626 if (jp->jobctl) {
4627 xtcsetpgrp(ttyfd, rootpid);
4628 /*
4629 * This is truly gross.
4630 * If we're doing job control, then we did a TIOCSPGRP which
4631 * caused us (the shell) to no longer be in the controlling
4632 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4633 * intuit from the subprocess exit status whether a SIGINT
4634 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4635 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004636 if (jp->sigint) /* TODO: do the same with all signals */
4637 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004638 }
4639 if (jp->state == JOBDONE)
4640#endif
4641 freejob(jp);
4642 return st;
4643}
4644
4645/*
4646 * return 1 if there are stopped jobs, otherwise 0
4647 */
4648static int
4649stoppedjobs(void)
4650{
4651 struct job *jp;
4652 int retval;
4653
4654 retval = 0;
4655 if (job_warning)
4656 goto out;
4657 jp = curjob;
4658 if (jp && jp->state == JOBSTOPPED) {
4659 out2str("You have stopped jobs.\n");
4660 job_warning = 2;
4661 retval++;
4662 }
4663 out:
4664 return retval;
4665}
4666
4667
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004668/* ============ redir.c
4669 *
4670 * Code for dealing with input/output redirection.
4671 */
4672
4673#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004674#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004675#ifndef PIPE_BUF
4676# define PIPESIZE 4096 /* amount of buffering in a pipe */
4677#else
4678# define PIPESIZE PIPE_BUF
4679#endif
4680
4681/*
4682 * Open a file in noclobber mode.
4683 * The code was copied from bash.
4684 */
4685static int
4686noclobberopen(const char *fname)
4687{
4688 int r, fd;
4689 struct stat finfo, finfo2;
4690
4691 /*
4692 * If the file exists and is a regular file, return an error
4693 * immediately.
4694 */
4695 r = stat(fname, &finfo);
4696 if (r == 0 && S_ISREG(finfo.st_mode)) {
4697 errno = EEXIST;
4698 return -1;
4699 }
4700
4701 /*
4702 * If the file was not present (r != 0), make sure we open it
4703 * exclusively so that if it is created before we open it, our open
4704 * will fail. Make sure that we do not truncate an existing file.
4705 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4706 * file was not a regular file, we leave O_EXCL off.
4707 */
4708 if (r != 0)
4709 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4710 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4711
4712 /* If the open failed, return the file descriptor right away. */
4713 if (fd < 0)
4714 return fd;
4715
4716 /*
4717 * OK, the open succeeded, but the file may have been changed from a
4718 * non-regular file to a regular file between the stat and the open.
4719 * We are assuming that the O_EXCL open handles the case where FILENAME
4720 * did not exist and is symlinked to an existing file between the stat
4721 * and open.
4722 */
4723
4724 /*
4725 * If we can open it and fstat the file descriptor, and neither check
4726 * revealed that it was a regular file, and the file has not been
4727 * replaced, return the file descriptor.
4728 */
4729 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4730 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4731 return fd;
4732
4733 /* The file has been replaced. badness. */
4734 close(fd);
4735 errno = EEXIST;
4736 return -1;
4737}
4738
4739/*
4740 * Handle here documents. Normally we fork off a process to write the
4741 * data to a pipe. If the document is short, we can stuff the data in
4742 * the pipe without forking.
4743 */
4744/* openhere needs this forward reference */
4745static void expandhere(union node *arg, int fd);
4746static int
4747openhere(union node *redir)
4748{
4749 int pip[2];
4750 size_t len = 0;
4751
4752 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004753 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004754 if (redir->type == NHERE) {
4755 len = strlen(redir->nhere.doc->narg.text);
4756 if (len <= PIPESIZE) {
4757 full_write(pip[1], redir->nhere.doc->narg.text, len);
4758 goto out;
4759 }
4760 }
4761 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4762 close(pip[0]);
4763 signal(SIGINT, SIG_IGN);
4764 signal(SIGQUIT, SIG_IGN);
4765 signal(SIGHUP, SIG_IGN);
4766#ifdef SIGTSTP
4767 signal(SIGTSTP, SIG_IGN);
4768#endif
4769 signal(SIGPIPE, SIG_DFL);
4770 if (redir->type == NHERE)
4771 full_write(pip[1], redir->nhere.doc->narg.text, len);
4772 else
4773 expandhere(redir->nhere.doc, pip[1]);
4774 _exit(0);
4775 }
4776 out:
4777 close(pip[1]);
4778 return pip[0];
4779}
4780
4781static int
4782openredirect(union node *redir)
4783{
4784 char *fname;
4785 int f;
4786
4787 switch (redir->nfile.type) {
4788 case NFROM:
4789 fname = redir->nfile.expfname;
4790 f = open(fname, O_RDONLY);
4791 if (f < 0)
4792 goto eopen;
4793 break;
4794 case NFROMTO:
4795 fname = redir->nfile.expfname;
4796 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4797 if (f < 0)
4798 goto ecreate;
4799 break;
4800 case NTO:
4801 /* Take care of noclobber mode. */
4802 if (Cflag) {
4803 fname = redir->nfile.expfname;
4804 f = noclobberopen(fname);
4805 if (f < 0)
4806 goto ecreate;
4807 break;
4808 }
4809 /* FALLTHROUGH */
4810 case NCLOBBER:
4811 fname = redir->nfile.expfname;
4812 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4813 if (f < 0)
4814 goto ecreate;
4815 break;
4816 case NAPPEND:
4817 fname = redir->nfile.expfname;
4818 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4819 if (f < 0)
4820 goto ecreate;
4821 break;
4822 default:
4823#if DEBUG
4824 abort();
4825#endif
4826 /* Fall through to eliminate warning. */
4827 case NTOFD:
4828 case NFROMFD:
4829 f = -1;
4830 break;
4831 case NHERE:
4832 case NXHERE:
4833 f = openhere(redir);
4834 break;
4835 }
4836
4837 return f;
4838 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004839 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004840 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004841 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004842}
4843
4844/*
4845 * Copy a file descriptor to be >= to. Returns -1
4846 * if the source file descriptor is closed, EMPTY if there are no unused
4847 * file descriptors left.
4848 */
4849static int
4850copyfd(int from, int to)
4851{
4852 int newfd;
4853
4854 newfd = fcntl(from, F_DUPFD, to);
4855 if (newfd < 0) {
4856 if (errno == EMFILE)
4857 return EMPTY;
4858 ash_msg_and_raise_error("%d: %m", from);
4859 }
4860 return newfd;
4861}
4862
4863static void
4864dupredirect(union node *redir, int f)
4865{
4866 int fd = redir->nfile.fd;
4867
4868 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4869 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4870 copyfd(redir->ndup.dupfd, fd);
4871 }
4872 return;
4873 }
4874
4875 if (f != fd) {
4876 copyfd(f, fd);
4877 close(f);
4878 }
4879}
4880
4881/*
4882 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4883 * old file descriptors are stashed away so that the redirection can be
4884 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4885 * standard output, and the standard error if it becomes a duplicate of
4886 * stdout, is saved in memory.
4887 */
4888/* flags passed to redirect */
4889#define REDIR_PUSH 01 /* save previous values of file descriptors */
4890#define REDIR_SAVEFD2 03 /* set preverrout */
4891static void
4892redirect(union node *redir, int flags)
4893{
4894 union node *n;
4895 struct redirtab *sv;
4896 int i;
4897 int fd;
4898 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004899
Denis Vlasenko01631112007-12-16 17:20:38 +00004900 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004901 if (!redir) {
4902 return;
4903 }
4904 sv = NULL;
4905 INT_OFF;
4906 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004907 sv = ckmalloc(sizeof(*sv));
4908 sv->next = redirlist;
4909 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004910 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004911 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004912 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004913 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004914 }
4915 n = redir;
4916 do {
4917 fd = n->nfile.fd;
4918 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4919 && n->ndup.dupfd == fd)
4920 continue; /* redirect from/to same file descriptor */
4921
4922 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004923 if (fd == newfd) {
4924 /* Descriptor wasn't open before redirect.
4925 * Mark it for close in the future */
4926 if (sv && sv->renamed[fd] == EMPTY)
4927 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004928 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004929 }
4930 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004931 i = fcntl(fd, F_DUPFD, 10);
4932
4933 if (i == -1) {
4934 i = errno;
4935 if (i != EBADF) {
4936 close(newfd);
4937 errno = i;
4938 ash_msg_and_raise_error("%d: %m", fd);
4939 /* NOTREACHED */
4940 }
4941 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004942 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004943 close(fd);
4944 }
4945 } else {
4946 close(fd);
4947 }
4948 dupredirect(n, newfd);
4949 } while ((n = n->nfile.next));
4950 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004951 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004952 preverrout_fd = sv->renamed[2];
4953}
4954
4955/*
4956 * Undo the effects of the last redirection.
4957 */
4958static void
4959popredir(int drop)
4960{
4961 struct redirtab *rp;
4962 int i;
4963
Denis Vlasenko01631112007-12-16 17:20:38 +00004964 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004965 return;
4966 INT_OFF;
4967 rp = redirlist;
4968 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004969 if (rp->renamed[i] == CLOSED) {
4970 if (!drop)
4971 close(i);
4972 continue;
4973 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004974 if (rp->renamed[i] != EMPTY) {
4975 if (!drop) {
4976 close(i);
4977 copyfd(rp->renamed[i], i);
4978 }
4979 close(rp->renamed[i]);
4980 }
4981 }
4982 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004983 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004984 free(rp);
4985 INT_ON;
4986}
4987
4988/*
4989 * Undo all redirections. Called on error or interrupt.
4990 */
4991
4992/*
4993 * Discard all saved file descriptors.
4994 */
4995static void
4996clearredir(int drop)
4997{
4998 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004999 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005000 if (!redirlist)
5001 break;
5002 popredir(drop);
5003 }
5004}
5005
5006static int
5007redirectsafe(union node *redir, int flags)
5008{
5009 int err;
5010 volatile int saveint;
5011 struct jmploc *volatile savehandler = exception_handler;
5012 struct jmploc jmploc;
5013
5014 SAVE_INT(saveint);
5015 err = setjmp(jmploc.loc) * 2;
5016 if (!err) {
5017 exception_handler = &jmploc;
5018 redirect(redir, flags);
5019 }
5020 exception_handler = savehandler;
5021 if (err && exception != EXERROR)
5022 longjmp(exception_handler->loc, 1);
5023 RESTORE_INT(saveint);
5024 return err;
5025}
5026
5027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005028/* ============ Routines to expand arguments to commands
5029 *
5030 * We have to deal with backquotes, shell variables, and file metacharacters.
5031 */
5032
5033/*
5034 * expandarg flags
5035 */
5036#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5037#define EXP_TILDE 0x2 /* do normal tilde expansion */
5038#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5039#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5040#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5041#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5042#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5043#define EXP_WORD 0x80 /* expand word in parameter expansion */
5044#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5045/*
5046 * _rmescape() flags
5047 */
5048#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5049#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5050#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5051#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5052#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5053
5054/*
5055 * Structure specifying which parts of the string should be searched
5056 * for IFS characters.
5057 */
5058struct ifsregion {
5059 struct ifsregion *next; /* next region in list */
5060 int begoff; /* offset of start of region */
5061 int endoff; /* offset of end of region */
5062 int nulonly; /* search for nul bytes only */
5063};
5064
5065struct arglist {
5066 struct strlist *list;
5067 struct strlist **lastp;
5068};
5069
5070/* output of current string */
5071static char *expdest;
5072/* list of back quote expressions */
5073static struct nodelist *argbackq;
5074/* first struct in list of ifs regions */
5075static struct ifsregion ifsfirst;
5076/* last struct in list */
5077static struct ifsregion *ifslastp;
5078/* holds expanded arg list */
5079static struct arglist exparg;
5080
5081/*
5082 * Our own itoa().
5083 */
5084static int
5085cvtnum(arith_t num)
5086{
5087 int len;
5088
5089 expdest = makestrspace(32, expdest);
5090#if ENABLE_ASH_MATH_SUPPORT_64
5091 len = fmtstr(expdest, 32, "%lld", (long long) num);
5092#else
5093 len = fmtstr(expdest, 32, "%ld", num);
5094#endif
5095 STADJUST(len, expdest);
5096 return len;
5097}
5098
5099static size_t
5100esclen(const char *start, const char *p)
5101{
5102 size_t esc = 0;
5103
5104 while (p > start && *--p == CTLESC) {
5105 esc++;
5106 }
5107 return esc;
5108}
5109
5110/*
5111 * Remove any CTLESC characters from a string.
5112 */
5113static char *
5114_rmescapes(char *str, int flag)
5115{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005116 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005117
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005118 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005119 unsigned inquotes;
5120 int notescaped;
5121 int globbing;
5122
5123 p = strpbrk(str, qchars);
5124 if (!p) {
5125 return str;
5126 }
5127 q = p;
5128 r = str;
5129 if (flag & RMESCAPE_ALLOC) {
5130 size_t len = p - str;
5131 size_t fulllen = len + strlen(p) + 1;
5132
5133 if (flag & RMESCAPE_GROW) {
5134 r = makestrspace(fulllen, expdest);
5135 } else if (flag & RMESCAPE_HEAP) {
5136 r = ckmalloc(fulllen);
5137 } else {
5138 r = stalloc(fulllen);
5139 }
5140 q = r;
5141 if (len > 0) {
5142 q = memcpy(q, str, len) + len;
5143 }
5144 }
5145 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5146 globbing = flag & RMESCAPE_GLOB;
5147 notescaped = globbing;
5148 while (*p) {
5149 if (*p == CTLQUOTEMARK) {
5150 inquotes = ~inquotes;
5151 p++;
5152 notescaped = globbing;
5153 continue;
5154 }
5155 if (*p == '\\') {
5156 /* naked back slash */
5157 notescaped = 0;
5158 goto copy;
5159 }
5160 if (*p == CTLESC) {
5161 p++;
5162 if (notescaped && inquotes && *p != '/') {
5163 *q++ = '\\';
5164 }
5165 }
5166 notescaped = globbing;
5167 copy:
5168 *q++ = *p++;
5169 }
5170 *q = '\0';
5171 if (flag & RMESCAPE_GROW) {
5172 expdest = r;
5173 STADJUST(q - r + 1, expdest);
5174 }
5175 return r;
5176}
5177#define rmescapes(p) _rmescapes((p), 0)
5178
5179#define pmatch(a, b) !fnmatch((a), (b), 0)
5180
5181/*
5182 * Prepare a pattern for a expmeta (internal glob(3)) call.
5183 *
5184 * Returns an stalloced string.
5185 */
5186static char *
5187preglob(const char *pattern, int quoted, int flag)
5188{
5189 flag |= RMESCAPE_GLOB;
5190 if (quoted) {
5191 flag |= RMESCAPE_QUOTED;
5192 }
5193 return _rmescapes((char *)pattern, flag);
5194}
5195
5196/*
5197 * Put a string on the stack.
5198 */
5199static void
5200memtodest(const char *p, size_t len, int syntax, int quotes)
5201{
5202 char *q = expdest;
5203
5204 q = makestrspace(len * 2, q);
5205
5206 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005207 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005208 if (!c)
5209 continue;
5210 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5211 USTPUTC(CTLESC, q);
5212 USTPUTC(c, q);
5213 }
5214
5215 expdest = q;
5216}
5217
5218static void
5219strtodest(const char *p, int syntax, int quotes)
5220{
5221 memtodest(p, strlen(p), syntax, quotes);
5222}
5223
5224/*
5225 * Record the fact that we have to scan this region of the
5226 * string for IFS characters.
5227 */
5228static void
5229recordregion(int start, int end, int nulonly)
5230{
5231 struct ifsregion *ifsp;
5232
5233 if (ifslastp == NULL) {
5234 ifsp = &ifsfirst;
5235 } else {
5236 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005237 ifsp = ckzalloc(sizeof(*ifsp));
5238 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005239 ifslastp->next = ifsp;
5240 INT_ON;
5241 }
5242 ifslastp = ifsp;
5243 ifslastp->begoff = start;
5244 ifslastp->endoff = end;
5245 ifslastp->nulonly = nulonly;
5246}
5247
5248static void
5249removerecordregions(int endoff)
5250{
5251 if (ifslastp == NULL)
5252 return;
5253
5254 if (ifsfirst.endoff > endoff) {
5255 while (ifsfirst.next != NULL) {
5256 struct ifsregion *ifsp;
5257 INT_OFF;
5258 ifsp = ifsfirst.next->next;
5259 free(ifsfirst.next);
5260 ifsfirst.next = ifsp;
5261 INT_ON;
5262 }
5263 if (ifsfirst.begoff > endoff)
5264 ifslastp = NULL;
5265 else {
5266 ifslastp = &ifsfirst;
5267 ifsfirst.endoff = endoff;
5268 }
5269 return;
5270 }
5271
5272 ifslastp = &ifsfirst;
5273 while (ifslastp->next && ifslastp->next->begoff < endoff)
5274 ifslastp=ifslastp->next;
5275 while (ifslastp->next != NULL) {
5276 struct ifsregion *ifsp;
5277 INT_OFF;
5278 ifsp = ifslastp->next->next;
5279 free(ifslastp->next);
5280 ifslastp->next = ifsp;
5281 INT_ON;
5282 }
5283 if (ifslastp->endoff > endoff)
5284 ifslastp->endoff = endoff;
5285}
5286
5287static char *
5288exptilde(char *startp, char *p, int flag)
5289{
5290 char c;
5291 char *name;
5292 struct passwd *pw;
5293 const char *home;
5294 int quotes = flag & (EXP_FULL | EXP_CASE);
5295 int startloc;
5296
5297 name = p + 1;
5298
5299 while ((c = *++p) != '\0') {
5300 switch (c) {
5301 case CTLESC:
5302 return startp;
5303 case CTLQUOTEMARK:
5304 return startp;
5305 case ':':
5306 if (flag & EXP_VARTILDE)
5307 goto done;
5308 break;
5309 case '/':
5310 case CTLENDVAR:
5311 goto done;
5312 }
5313 }
5314 done:
5315 *p = '\0';
5316 if (*name == '\0') {
5317 home = lookupvar(homestr);
5318 } else {
5319 pw = getpwnam(name);
5320 if (pw == NULL)
5321 goto lose;
5322 home = pw->pw_dir;
5323 }
5324 if (!home || !*home)
5325 goto lose;
5326 *p = c;
5327 startloc = expdest - (char *)stackblock();
5328 strtodest(home, SQSYNTAX, quotes);
5329 recordregion(startloc, expdest - (char *)stackblock(), 0);
5330 return p;
5331 lose:
5332 *p = c;
5333 return startp;
5334}
5335
5336/*
5337 * Execute a command inside back quotes. If it's a builtin command, we
5338 * want to save its output in a block obtained from malloc. Otherwise
5339 * we fork off a subprocess and get the output of the command via a pipe.
5340 * Should be called with interrupts off.
5341 */
5342struct backcmd { /* result of evalbackcmd */
5343 int fd; /* file descriptor to read from */
5344 char *buf; /* buffer */
5345 int nleft; /* number of chars in buffer */
5346 struct job *jp; /* job structure for command */
5347};
5348
5349/* These forward decls are needed to use "eval" code for backticks handling: */
5350static int back_exitstatus; /* exit status of backquoted command */
5351#define EV_EXIT 01 /* exit after evaluating tree */
5352static void evaltree(union node *, int);
5353
5354static void
5355evalbackcmd(union node *n, struct backcmd *result)
5356{
5357 int saveherefd;
5358
5359 result->fd = -1;
5360 result->buf = NULL;
5361 result->nleft = 0;
5362 result->jp = NULL;
5363 if (n == NULL) {
5364 goto out;
5365 }
5366
5367 saveherefd = herefd;
5368 herefd = -1;
5369
5370 {
5371 int pip[2];
5372 struct job *jp;
5373
5374 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005375 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005376 jp = makejob(n, 1);
5377 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5378 FORCE_INT_ON;
5379 close(pip[0]);
5380 if (pip[1] != 1) {
5381 close(1);
5382 copyfd(pip[1], 1);
5383 close(pip[1]);
5384 }
5385 eflag = 0;
5386 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5387 /* NOTREACHED */
5388 }
5389 close(pip[1]);
5390 result->fd = pip[0];
5391 result->jp = jp;
5392 }
5393 herefd = saveherefd;
5394 out:
5395 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5396 result->fd, result->buf, result->nleft, result->jp));
5397}
5398
5399/*
5400 * Expand stuff in backwards quotes.
5401 */
5402static void
5403expbackq(union node *cmd, int quoted, int quotes)
5404{
5405 struct backcmd in;
5406 int i;
5407 char buf[128];
5408 char *p;
5409 char *dest;
5410 int startloc;
5411 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5412 struct stackmark smark;
5413
5414 INT_OFF;
5415 setstackmark(&smark);
5416 dest = expdest;
5417 startloc = dest - (char *)stackblock();
5418 grabstackstr(dest);
5419 evalbackcmd(cmd, &in);
5420 popstackmark(&smark);
5421
5422 p = in.buf;
5423 i = in.nleft;
5424 if (i == 0)
5425 goto read;
5426 for (;;) {
5427 memtodest(p, i, syntax, quotes);
5428 read:
5429 if (in.fd < 0)
5430 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005431 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005432 TRACE(("expbackq: read returns %d\n", i));
5433 if (i <= 0)
5434 break;
5435 p = buf;
5436 }
5437
Denis Vlasenko60818682007-09-28 22:07:23 +00005438 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005439 if (in.fd >= 0) {
5440 close(in.fd);
5441 back_exitstatus = waitforjob(in.jp);
5442 }
5443 INT_ON;
5444
5445 /* Eat all trailing newlines */
5446 dest = expdest;
5447 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5448 STUNPUTC(dest);
5449 expdest = dest;
5450
5451 if (quoted == 0)
5452 recordregion(startloc, dest - (char *)stackblock(), 0);
5453 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5454 (dest - (char *)stackblock()) - startloc,
5455 (dest - (char *)stackblock()) - startloc,
5456 stackblock() + startloc));
5457}
5458
5459#if ENABLE_ASH_MATH_SUPPORT
5460/*
5461 * Expand arithmetic expression. Backup to start of expression,
5462 * evaluate, place result in (backed up) result, adjust string position.
5463 */
5464static void
5465expari(int quotes)
5466{
5467 char *p, *start;
5468 int begoff;
5469 int flag;
5470 int len;
5471
5472 /* ifsfree(); */
5473
5474 /*
5475 * This routine is slightly over-complicated for
5476 * efficiency. Next we scan backwards looking for the
5477 * start of arithmetic.
5478 */
5479 start = stackblock();
5480 p = expdest - 1;
5481 *p = '\0';
5482 p--;
5483 do {
5484 int esc;
5485
5486 while (*p != CTLARI) {
5487 p--;
5488#if DEBUG
5489 if (p < start) {
5490 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5491 }
5492#endif
5493 }
5494
5495 esc = esclen(start, p);
5496 if (!(esc % 2)) {
5497 break;
5498 }
5499
5500 p -= esc + 1;
5501 } while (1);
5502
5503 begoff = p - start;
5504
5505 removerecordregions(begoff);
5506
5507 flag = p[1];
5508
5509 expdest = p;
5510
5511 if (quotes)
5512 rmescapes(p + 2);
5513
5514 len = cvtnum(dash_arith(p + 2));
5515
5516 if (flag != '"')
5517 recordregion(begoff, begoff + len, 0);
5518}
5519#endif
5520
5521/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005522static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005523
5524/*
5525 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5526 * characters to allow for further processing. Otherwise treat
5527 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005528 *
5529 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5530 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5531 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005532 */
5533static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005534argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005535{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005536 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005537 '=',
5538 ':',
5539 CTLQUOTEMARK,
5540 CTLENDVAR,
5541 CTLESC,
5542 CTLVAR,
5543 CTLBACKQ,
5544 CTLBACKQ | CTLQUOTE,
5545#if ENABLE_ASH_MATH_SUPPORT
5546 CTLENDARI,
5547#endif
5548 0
5549 };
5550 const char *reject = spclchars;
5551 int c;
5552 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5553 int breakall = flag & EXP_WORD;
5554 int inquotes;
5555 size_t length;
5556 int startloc;
5557
5558 if (!(flag & EXP_VARTILDE)) {
5559 reject += 2;
5560 } else if (flag & EXP_VARTILDE2) {
5561 reject++;
5562 }
5563 inquotes = 0;
5564 length = 0;
5565 if (flag & EXP_TILDE) {
5566 char *q;
5567
5568 flag &= ~EXP_TILDE;
5569 tilde:
5570 q = p;
5571 if (*q == CTLESC && (flag & EXP_QWORD))
5572 q++;
5573 if (*q == '~')
5574 p = exptilde(p, q, flag);
5575 }
5576 start:
5577 startloc = expdest - (char *)stackblock();
5578 for (;;) {
5579 length += strcspn(p + length, reject);
5580 c = p[length];
5581 if (c && (!(c & 0x80)
5582#if ENABLE_ASH_MATH_SUPPORT
5583 || c == CTLENDARI
5584#endif
5585 )) {
5586 /* c == '=' || c == ':' || c == CTLENDARI */
5587 length++;
5588 }
5589 if (length > 0) {
5590 int newloc;
5591 expdest = stack_nputstr(p, length, expdest);
5592 newloc = expdest - (char *)stackblock();
5593 if (breakall && !inquotes && newloc > startloc) {
5594 recordregion(startloc, newloc, 0);
5595 }
5596 startloc = newloc;
5597 }
5598 p += length + 1;
5599 length = 0;
5600
5601 switch (c) {
5602 case '\0':
5603 goto breakloop;
5604 case '=':
5605 if (flag & EXP_VARTILDE2) {
5606 p--;
5607 continue;
5608 }
5609 flag |= EXP_VARTILDE2;
5610 reject++;
5611 /* fall through */
5612 case ':':
5613 /*
5614 * sort of a hack - expand tildes in variable
5615 * assignments (after the first '=' and after ':'s).
5616 */
5617 if (*--p == '~') {
5618 goto tilde;
5619 }
5620 continue;
5621 }
5622
5623 switch (c) {
5624 case CTLENDVAR: /* ??? */
5625 goto breakloop;
5626 case CTLQUOTEMARK:
5627 /* "$@" syntax adherence hack */
5628 if (
5629 !inquotes &&
5630 !memcmp(p, dolatstr, 4) &&
5631 (p[4] == CTLQUOTEMARK || (
5632 p[4] == CTLENDVAR &&
5633 p[5] == CTLQUOTEMARK
5634 ))
5635 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005636 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005637 goto start;
5638 }
5639 inquotes = !inquotes;
5640 addquote:
5641 if (quotes) {
5642 p--;
5643 length++;
5644 startloc++;
5645 }
5646 break;
5647 case CTLESC:
5648 startloc++;
5649 length++;
5650 goto addquote;
5651 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005652 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005653 goto start;
5654 case CTLBACKQ:
5655 c = 0;
5656 case CTLBACKQ|CTLQUOTE:
5657 expbackq(argbackq->n, c, quotes);
5658 argbackq = argbackq->next;
5659 goto start;
5660#if ENABLE_ASH_MATH_SUPPORT
5661 case CTLENDARI:
5662 p--;
5663 expari(quotes);
5664 goto start;
5665#endif
5666 }
5667 }
5668 breakloop:
5669 ;
5670}
5671
5672static char *
5673scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5674 int zero)
5675{
5676 char *loc;
5677 char *loc2;
5678 char c;
5679
5680 loc = startp;
5681 loc2 = rmesc;
5682 do {
5683 int match;
5684 const char *s = loc2;
5685 c = *loc2;
5686 if (zero) {
5687 *loc2 = '\0';
5688 s = rmesc;
5689 }
5690 match = pmatch(str, s);
5691 *loc2 = c;
5692 if (match)
5693 return loc;
5694 if (quotes && *loc == CTLESC)
5695 loc++;
5696 loc++;
5697 loc2++;
5698 } while (c);
5699 return 0;
5700}
5701
5702static char *
5703scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5704 int zero)
5705{
5706 int esc = 0;
5707 char *loc;
5708 char *loc2;
5709
5710 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5711 int match;
5712 char c = *loc2;
5713 const char *s = loc2;
5714 if (zero) {
5715 *loc2 = '\0';
5716 s = rmesc;
5717 }
5718 match = pmatch(str, s);
5719 *loc2 = c;
5720 if (match)
5721 return loc;
5722 loc--;
5723 if (quotes) {
5724 if (--esc < 0) {
5725 esc = esclen(startp, loc);
5726 }
5727 if (esc % 2) {
5728 esc--;
5729 loc--;
5730 }
5731 }
5732 }
5733 return 0;
5734}
5735
5736static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5737static void
5738varunset(const char *end, const char *var, const char *umsg, int varflags)
5739{
5740 const char *msg;
5741 const char *tail;
5742
5743 tail = nullstr;
5744 msg = "parameter not set";
5745 if (umsg) {
5746 if (*end == CTLENDVAR) {
5747 if (varflags & VSNUL)
5748 tail = " or null";
5749 } else
5750 msg = umsg;
5751 }
5752 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5753}
5754
5755static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005756subevalvar(char *p, char *str, int strloc, int subtype,
5757 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005758{
5759 char *startp;
5760 char *loc;
5761 int saveherefd = herefd;
5762 struct nodelist *saveargbackq = argbackq;
5763 int amount;
5764 char *rmesc, *rmescend;
5765 int zero;
5766 char *(*scan)(char *, char *, char *, char *, int , int);
5767
5768 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005769 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5770 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005771 STPUTC('\0', expdest);
5772 herefd = saveherefd;
5773 argbackq = saveargbackq;
5774 startp = stackblock() + startloc;
5775
5776 switch (subtype) {
5777 case VSASSIGN:
5778 setvar(str, startp, 0);
5779 amount = startp - expdest;
5780 STADJUST(amount, expdest);
5781 return startp;
5782
5783 case VSQUESTION:
5784 varunset(p, str, startp, varflags);
5785 /* NOTREACHED */
5786 }
5787
5788 subtype -= VSTRIMRIGHT;
5789#if DEBUG
5790 if (subtype < 0 || subtype > 3)
5791 abort();
5792#endif
5793
5794 rmesc = startp;
5795 rmescend = stackblock() + strloc;
5796 if (quotes) {
5797 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5798 if (rmesc != startp) {
5799 rmescend = expdest;
5800 startp = stackblock() + startloc;
5801 }
5802 }
5803 rmescend--;
5804 str = stackblock() + strloc;
5805 preglob(str, varflags & VSQUOTE, 0);
5806
5807 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5808 zero = subtype >> 1;
5809 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5810 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5811
5812 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5813 if (loc) {
5814 if (zero) {
5815 memmove(startp, loc, str - loc);
5816 loc = startp + (str - loc) - 1;
5817 }
5818 *loc = '\0';
5819 amount = loc - expdest;
5820 STADJUST(amount, expdest);
5821 }
5822 return loc;
5823}
5824
5825/*
5826 * Add the value of a specialized variable to the stack string.
5827 */
5828static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005829varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830{
5831 int num;
5832 char *p;
5833 int i;
5834 int sep = 0;
5835 int sepq = 0;
5836 ssize_t len = 0;
5837 char **ap;
5838 int syntax;
5839 int quoted = varflags & VSQUOTE;
5840 int subtype = varflags & VSTYPE;
5841 int quotes = flags & (EXP_FULL | EXP_CASE);
5842
5843 if (quoted && (flags & EXP_FULL))
5844 sep = 1 << CHAR_BIT;
5845
5846 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5847 switch (*name) {
5848 case '$':
5849 num = rootpid;
5850 goto numvar;
5851 case '?':
5852 num = exitstatus;
5853 goto numvar;
5854 case '#':
5855 num = shellparam.nparam;
5856 goto numvar;
5857 case '!':
5858 num = backgndpid;
5859 if (num == 0)
5860 return -1;
5861 numvar:
5862 len = cvtnum(num);
5863 break;
5864 case '-':
5865 p = makestrspace(NOPTS, expdest);
5866 for (i = NOPTS - 1; i >= 0; i--) {
5867 if (optlist[i]) {
5868 USTPUTC(optletters(i), p);
5869 len++;
5870 }
5871 }
5872 expdest = p;
5873 break;
5874 case '@':
5875 if (sep)
5876 goto param;
5877 /* fall through */
5878 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005879 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005880 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5881 sepq = 1;
5882 param:
5883 ap = shellparam.p;
5884 if (!ap)
5885 return -1;
5886 while ((p = *ap++)) {
5887 size_t partlen;
5888
5889 partlen = strlen(p);
5890 len += partlen;
5891
5892 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5893 memtodest(p, partlen, syntax, quotes);
5894
5895 if (*ap && sep) {
5896 char *q;
5897
5898 len++;
5899 if (subtype == VSPLUS || subtype == VSLENGTH) {
5900 continue;
5901 }
5902 q = expdest;
5903 if (sepq)
5904 STPUTC(CTLESC, q);
5905 STPUTC(sep, q);
5906 expdest = q;
5907 }
5908 }
5909 return len;
5910 case '0':
5911 case '1':
5912 case '2':
5913 case '3':
5914 case '4':
5915 case '5':
5916 case '6':
5917 case '7':
5918 case '8':
5919 case '9':
5920 num = atoi(name);
5921 if (num < 0 || num > shellparam.nparam)
5922 return -1;
5923 p = num ? shellparam.p[num - 1] : arg0;
5924 goto value;
5925 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005926 /* NB: name has form "VAR=..." */
5927
5928 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
5929 * which should be considered before we check variables. */
5930 if (var_str_list) {
5931 unsigned name_len = (strchrnul(name, '=') - name) + 1;
5932 p = NULL;
5933 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00005934 char *str, *eq;
5935 str = var_str_list->text;
5936 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005937 if (!eq) /* stop at first non-assignment */
5938 break;
5939 eq++;
5940 if (name_len == (eq - str)
5941 && strncmp(str, name, name_len) == 0) {
5942 p = eq;
5943 /* goto value; - WRONG! */
5944 /* think "A=1 A=2 B=$A" */
5945 }
5946 var_str_list = var_str_list->next;
5947 } while (var_str_list);
5948 if (p)
5949 goto value;
5950 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951 p = lookupvar(name);
5952 value:
5953 if (!p)
5954 return -1;
5955
5956 len = strlen(p);
5957 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5958 memtodest(p, len, syntax, quotes);
5959 return len;
5960 }
5961
5962 if (subtype == VSPLUS || subtype == VSLENGTH)
5963 STADJUST(-len, expdest);
5964 return len;
5965}
5966
5967/*
5968 * Expand a variable, and return a pointer to the next character in the
5969 * input string.
5970 */
5971static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005972evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005973{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005974 char varflags;
5975 char subtype;
5976 char quoted;
5977 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005978 char *var;
5979 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005980 int startloc;
5981 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005982
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005983 varflags = *p++;
5984 subtype = varflags & VSTYPE;
5985 quoted = varflags & VSQUOTE;
5986 var = p;
5987 easy = (!quoted || (*var == '@' && shellparam.nparam));
5988 startloc = expdest - (char *)stackblock();
5989 p = strchr(p, '=') + 1;
5990
5991 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005992 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005993 if (varflags & VSNUL)
5994 varlen--;
5995
5996 if (subtype == VSPLUS) {
5997 varlen = -1 - varlen;
5998 goto vsplus;
5999 }
6000
6001 if (subtype == VSMINUS) {
6002 vsplus:
6003 if (varlen < 0) {
6004 argstr(
6005 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006006 (quoted ? EXP_QWORD : EXP_WORD),
6007 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006008 );
6009 goto end;
6010 }
6011 if (easy)
6012 goto record;
6013 goto end;
6014 }
6015
6016 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6017 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006018 if (subevalvar(p, var, /* strloc: */ 0,
6019 subtype, startloc, varflags,
6020 /* quotes: */ 0,
6021 var_str_list)
6022 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 varflags &= ~VSNUL;
6024 /*
6025 * Remove any recorded regions beyond
6026 * start of variable
6027 */
6028 removerecordregions(startloc);
6029 goto again;
6030 }
6031 goto end;
6032 }
6033 if (easy)
6034 goto record;
6035 goto end;
6036 }
6037
6038 if (varlen < 0 && uflag)
6039 varunset(p, var, 0, 0);
6040
6041 if (subtype == VSLENGTH) {
6042 cvtnum(varlen > 0 ? varlen : 0);
6043 goto record;
6044 }
6045
6046 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006047 if (easy)
6048 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006049 goto end;
6050 }
6051
6052#if DEBUG
6053 switch (subtype) {
6054 case VSTRIMLEFT:
6055 case VSTRIMLEFTMAX:
6056 case VSTRIMRIGHT:
6057 case VSTRIMRIGHTMAX:
6058 break;
6059 default:
6060 abort();
6061 }
6062#endif
6063
6064 if (varlen >= 0) {
6065 /*
6066 * Terminate the string and start recording the pattern
6067 * right after it
6068 */
6069 STPUTC('\0', expdest);
6070 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006071 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6072 startloc, varflags,
6073 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6074 var_str_list)
6075 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006076 int amount = expdest - (
6077 (char *)stackblock() + patloc - 1
6078 );
6079 STADJUST(-amount, expdest);
6080 }
6081 /* Remove any recorded regions beyond start of variable */
6082 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006083 record:
6084 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006085 }
6086
6087 end:
6088 if (subtype != VSNORMAL) { /* skip to end of alternative */
6089 int nesting = 1;
6090 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006091 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 if (c == CTLESC)
6093 p++;
6094 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6095 if (varlen >= 0)
6096 argbackq = argbackq->next;
6097 } else if (c == CTLVAR) {
6098 if ((*p++ & VSTYPE) != VSNORMAL)
6099 nesting++;
6100 } else if (c == CTLENDVAR) {
6101 if (--nesting == 0)
6102 break;
6103 }
6104 }
6105 }
6106 return p;
6107}
6108
6109/*
6110 * Break the argument string into pieces based upon IFS and add the
6111 * strings to the argument list. The regions of the string to be
6112 * searched for IFS characters have been stored by recordregion.
6113 */
6114static void
6115ifsbreakup(char *string, struct arglist *arglist)
6116{
6117 struct ifsregion *ifsp;
6118 struct strlist *sp;
6119 char *start;
6120 char *p;
6121 char *q;
6122 const char *ifs, *realifs;
6123 int ifsspc;
6124 int nulonly;
6125
6126 start = string;
6127 if (ifslastp != NULL) {
6128 ifsspc = 0;
6129 nulonly = 0;
6130 realifs = ifsset() ? ifsval() : defifs;
6131 ifsp = &ifsfirst;
6132 do {
6133 p = string + ifsp->begoff;
6134 nulonly = ifsp->nulonly;
6135 ifs = nulonly ? nullstr : realifs;
6136 ifsspc = 0;
6137 while (p < string + ifsp->endoff) {
6138 q = p;
6139 if (*p == CTLESC)
6140 p++;
6141 if (!strchr(ifs, *p)) {
6142 p++;
6143 continue;
6144 }
6145 if (!nulonly)
6146 ifsspc = (strchr(defifs, *p) != NULL);
6147 /* Ignore IFS whitespace at start */
6148 if (q == start && ifsspc) {
6149 p++;
6150 start = p;
6151 continue;
6152 }
6153 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006154 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006155 sp->text = start;
6156 *arglist->lastp = sp;
6157 arglist->lastp = &sp->next;
6158 p++;
6159 if (!nulonly) {
6160 for (;;) {
6161 if (p >= string + ifsp->endoff) {
6162 break;
6163 }
6164 q = p;
6165 if (*p == CTLESC)
6166 p++;
6167 if (strchr(ifs, *p) == NULL ) {
6168 p = q;
6169 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006170 }
6171 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006172 if (ifsspc) {
6173 p++;
6174 ifsspc = 0;
6175 } else {
6176 p = q;
6177 break;
6178 }
6179 } else
6180 p++;
6181 }
6182 }
6183 start = p;
6184 } /* while */
6185 ifsp = ifsp->next;
6186 } while (ifsp != NULL);
6187 if (nulonly)
6188 goto add;
6189 }
6190
6191 if (!*start)
6192 return;
6193
6194 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006195 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006196 sp->text = start;
6197 *arglist->lastp = sp;
6198 arglist->lastp = &sp->next;
6199}
6200
6201static void
6202ifsfree(void)
6203{
6204 struct ifsregion *p;
6205
6206 INT_OFF;
6207 p = ifsfirst.next;
6208 do {
6209 struct ifsregion *ifsp;
6210 ifsp = p->next;
6211 free(p);
6212 p = ifsp;
6213 } while (p);
6214 ifslastp = NULL;
6215 ifsfirst.next = NULL;
6216 INT_ON;
6217}
6218
6219/*
6220 * Add a file name to the list.
6221 */
6222static void
6223addfname(const char *name)
6224{
6225 struct strlist *sp;
6226
Denis Vlasenko597906c2008-02-20 16:38:54 +00006227 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006228 sp->text = ststrdup(name);
6229 *exparg.lastp = sp;
6230 exparg.lastp = &sp->next;
6231}
6232
6233static char *expdir;
6234
6235/*
6236 * Do metacharacter (i.e. *, ?, [...]) expansion.
6237 */
6238static void
6239expmeta(char *enddir, char *name)
6240{
6241 char *p;
6242 const char *cp;
6243 char *start;
6244 char *endname;
6245 int metaflag;
6246 struct stat statb;
6247 DIR *dirp;
6248 struct dirent *dp;
6249 int atend;
6250 int matchdot;
6251
6252 metaflag = 0;
6253 start = name;
6254 for (p = name; *p; p++) {
6255 if (*p == '*' || *p == '?')
6256 metaflag = 1;
6257 else if (*p == '[') {
6258 char *q = p + 1;
6259 if (*q == '!')
6260 q++;
6261 for (;;) {
6262 if (*q == '\\')
6263 q++;
6264 if (*q == '/' || *q == '\0')
6265 break;
6266 if (*++q == ']') {
6267 metaflag = 1;
6268 break;
6269 }
6270 }
6271 } else if (*p == '\\')
6272 p++;
6273 else if (*p == '/') {
6274 if (metaflag)
6275 goto out;
6276 start = p + 1;
6277 }
6278 }
6279 out:
6280 if (metaflag == 0) { /* we've reached the end of the file name */
6281 if (enddir != expdir)
6282 metaflag++;
6283 p = name;
6284 do {
6285 if (*p == '\\')
6286 p++;
6287 *enddir++ = *p;
6288 } while (*p++);
6289 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6290 addfname(expdir);
6291 return;
6292 }
6293 endname = p;
6294 if (name < start) {
6295 p = name;
6296 do {
6297 if (*p == '\\')
6298 p++;
6299 *enddir++ = *p++;
6300 } while (p < start);
6301 }
6302 if (enddir == expdir) {
6303 cp = ".";
6304 } else if (enddir == expdir + 1 && *expdir == '/') {
6305 cp = "/";
6306 } else {
6307 cp = expdir;
6308 enddir[-1] = '\0';
6309 }
6310 dirp = opendir(cp);
6311 if (dirp == NULL)
6312 return;
6313 if (enddir != expdir)
6314 enddir[-1] = '/';
6315 if (*endname == 0) {
6316 atend = 1;
6317 } else {
6318 atend = 0;
6319 *endname++ = '\0';
6320 }
6321 matchdot = 0;
6322 p = start;
6323 if (*p == '\\')
6324 p++;
6325 if (*p == '.')
6326 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006327 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006328 if (dp->d_name[0] == '.' && ! matchdot)
6329 continue;
6330 if (pmatch(start, dp->d_name)) {
6331 if (atend) {
6332 strcpy(enddir, dp->d_name);
6333 addfname(expdir);
6334 } else {
6335 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6336 continue;
6337 p[-1] = '/';
6338 expmeta(p, endname);
6339 }
6340 }
6341 }
6342 closedir(dirp);
6343 if (! atend)
6344 endname[-1] = '/';
6345}
6346
6347static struct strlist *
6348msort(struct strlist *list, int len)
6349{
6350 struct strlist *p, *q = NULL;
6351 struct strlist **lpp;
6352 int half;
6353 int n;
6354
6355 if (len <= 1)
6356 return list;
6357 half = len >> 1;
6358 p = list;
6359 for (n = half; --n >= 0; ) {
6360 q = p;
6361 p = p->next;
6362 }
6363 q->next = NULL; /* terminate first half of list */
6364 q = msort(list, half); /* sort first half of list */
6365 p = msort(p, len - half); /* sort second half */
6366 lpp = &list;
6367 for (;;) {
6368#if ENABLE_LOCALE_SUPPORT
6369 if (strcoll(p->text, q->text) < 0)
6370#else
6371 if (strcmp(p->text, q->text) < 0)
6372#endif
6373 {
6374 *lpp = p;
6375 lpp = &p->next;
6376 p = *lpp;
6377 if (p == NULL) {
6378 *lpp = q;
6379 break;
6380 }
6381 } else {
6382 *lpp = q;
6383 lpp = &q->next;
6384 q = *lpp;
6385 if (q == NULL) {
6386 *lpp = p;
6387 break;
6388 }
6389 }
6390 }
6391 return list;
6392}
6393
6394/*
6395 * Sort the results of file name expansion. It calculates the number of
6396 * strings to sort and then calls msort (short for merge sort) to do the
6397 * work.
6398 */
6399static struct strlist *
6400expsort(struct strlist *str)
6401{
6402 int len;
6403 struct strlist *sp;
6404
6405 len = 0;
6406 for (sp = str; sp; sp = sp->next)
6407 len++;
6408 return msort(str, len);
6409}
6410
6411static void
6412expandmeta(struct strlist *str, int flag)
6413{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006414 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415 '*', '?', '[', 0
6416 };
6417 /* TODO - EXP_REDIR */
6418
6419 while (str) {
6420 struct strlist **savelastp;
6421 struct strlist *sp;
6422 char *p;
6423
6424 if (fflag)
6425 goto nometa;
6426 if (!strpbrk(str->text, metachars))
6427 goto nometa;
6428 savelastp = exparg.lastp;
6429
6430 INT_OFF;
6431 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6432 {
6433 int i = strlen(str->text);
6434 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6435 }
6436
6437 expmeta(expdir, p);
6438 free(expdir);
6439 if (p != str->text)
6440 free(p);
6441 INT_ON;
6442 if (exparg.lastp == savelastp) {
6443 /*
6444 * no matches
6445 */
6446 nometa:
6447 *exparg.lastp = str;
6448 rmescapes(str->text);
6449 exparg.lastp = &str->next;
6450 } else {
6451 *exparg.lastp = NULL;
6452 *savelastp = sp = expsort(*savelastp);
6453 while (sp->next != NULL)
6454 sp = sp->next;
6455 exparg.lastp = &sp->next;
6456 }
6457 str = str->next;
6458 }
6459}
6460
6461/*
6462 * Perform variable substitution and command substitution on an argument,
6463 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6464 * perform splitting and file name expansion. When arglist is NULL, perform
6465 * here document expansion.
6466 */
6467static void
6468expandarg(union node *arg, struct arglist *arglist, int flag)
6469{
6470 struct strlist *sp;
6471 char *p;
6472
6473 argbackq = arg->narg.backquote;
6474 STARTSTACKSTR(expdest);
6475 ifsfirst.next = NULL;
6476 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006477 argstr(arg->narg.text, flag,
6478 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006479 p = _STPUTC('\0', expdest);
6480 expdest = p - 1;
6481 if (arglist == NULL) {
6482 return; /* here document expanded */
6483 }
6484 p = grabstackstr(p);
6485 exparg.lastp = &exparg.list;
6486 /*
6487 * TODO - EXP_REDIR
6488 */
6489 if (flag & EXP_FULL) {
6490 ifsbreakup(p, &exparg);
6491 *exparg.lastp = NULL;
6492 exparg.lastp = &exparg.list;
6493 expandmeta(exparg.list, flag);
6494 } else {
6495 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6496 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006497 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006498 sp->text = p;
6499 *exparg.lastp = sp;
6500 exparg.lastp = &sp->next;
6501 }
6502 if (ifsfirst.next)
6503 ifsfree();
6504 *exparg.lastp = NULL;
6505 if (exparg.list) {
6506 *arglist->lastp = exparg.list;
6507 arglist->lastp = exparg.lastp;
6508 }
6509}
6510
6511/*
6512 * Expand shell variables and backquotes inside a here document.
6513 */
6514static void
6515expandhere(union node *arg, int fd)
6516{
6517 herefd = fd;
6518 expandarg(arg, (struct arglist *)NULL, 0);
6519 full_write(fd, stackblock(), expdest - (char *)stackblock());
6520}
6521
6522/*
6523 * Returns true if the pattern matches the string.
6524 */
6525static int
6526patmatch(char *pattern, const char *string)
6527{
6528 return pmatch(preglob(pattern, 0, 0), string);
6529}
6530
6531/*
6532 * See if a pattern matches in a case statement.
6533 */
6534static int
6535casematch(union node *pattern, char *val)
6536{
6537 struct stackmark smark;
6538 int result;
6539
6540 setstackmark(&smark);
6541 argbackq = pattern->narg.backquote;
6542 STARTSTACKSTR(expdest);
6543 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006544 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6545 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006546 STACKSTRNUL(expdest);
6547 result = patmatch(stackblock(), val);
6548 popstackmark(&smark);
6549 return result;
6550}
6551
6552
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006553/* ============ find_command */
6554
6555struct builtincmd {
6556 const char *name;
6557 int (*builtin)(int, char **);
6558 /* unsigned flags; */
6559};
6560#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006561/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006562 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006563#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006564#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006565
6566struct cmdentry {
6567 int cmdtype;
6568 union param {
6569 int index;
6570 const struct builtincmd *cmd;
6571 struct funcnode *func;
6572 } u;
6573};
6574/* values of cmdtype */
6575#define CMDUNKNOWN -1 /* no entry in table for command */
6576#define CMDNORMAL 0 /* command is an executable program */
6577#define CMDFUNCTION 1 /* command is a shell function */
6578#define CMDBUILTIN 2 /* command is a shell builtin */
6579
6580/* action to find_command() */
6581#define DO_ERR 0x01 /* prints errors */
6582#define DO_ABS 0x02 /* checks absolute paths */
6583#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6584#define DO_ALTPATH 0x08 /* using alternate path */
6585#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6586
6587static void find_command(char *, struct cmdentry *, int, const char *);
6588
6589
6590/* ============ Hashing commands */
6591
6592/*
6593 * When commands are first encountered, they are entered in a hash table.
6594 * This ensures that a full path search will not have to be done for them
6595 * on each invocation.
6596 *
6597 * We should investigate converting to a linear search, even though that
6598 * would make the command name "hash" a misnomer.
6599 */
6600
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006601#define ARB 1 /* actual size determined at run time */
6602
6603struct tblentry {
6604 struct tblentry *next; /* next entry in hash chain */
6605 union param param; /* definition of builtin function */
6606 short cmdtype; /* index identifying command */
6607 char rehash; /* if set, cd done since entry created */
6608 char cmdname[ARB]; /* name of command */
6609};
6610
Denis Vlasenko01631112007-12-16 17:20:38 +00006611static struct tblentry **cmdtable;
6612#define INIT_G_cmdtable() do { \
6613 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6614} while (0)
6615
6616static int builtinloc = -1; /* index in path of %builtin, or -1 */
6617
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006618
6619static void
6620tryexec(char *cmd, char **argv, char **envp)
6621{
6622 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006623
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006624#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006625 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006626 int a = find_applet_by_name(cmd);
6627 if (a >= 0) {
6628 if (APPLET_IS_NOEXEC(a))
6629 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006630 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006631 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006632 /* If they called chroot or otherwise made the binary no longer
6633 * executable, fall through */
6634 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006635 }
6636#endif
6637
6638 repeat:
6639#ifdef SYSV
6640 do {
6641 execve(cmd, argv, envp);
6642 } while (errno == EINTR);
6643#else
6644 execve(cmd, argv, envp);
6645#endif
6646 if (repeated++) {
6647 free(argv);
6648 } else if (errno == ENOEXEC) {
6649 char **ap;
6650 char **new;
6651
6652 for (ap = argv; *ap; ap++)
6653 ;
6654 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6655 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006656 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006657 ap += 2;
6658 argv++;
6659 while ((*ap++ = *argv++))
Denis Vlasenko597906c2008-02-20 16:38:54 +00006660 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006661 argv = new;
6662 goto repeat;
6663 }
6664}
6665
6666/*
6667 * Exec a program. Never returns. If you change this routine, you may
6668 * have to change the find_command routine as well.
6669 */
6670#define environment() listvars(VEXPORT, VUNSET, 0)
6671static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6672static void
6673shellexec(char **argv, const char *path, int idx)
6674{
6675 char *cmdname;
6676 int e;
6677 char **envp;
6678 int exerrno;
6679
6680 clearredir(1);
6681 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006682 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006683#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006684 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006685#endif
6686 ) {
6687 tryexec(argv[0], argv, envp);
6688 e = errno;
6689 } else {
6690 e = ENOENT;
6691 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6692 if (--idx < 0 && pathopt == NULL) {
6693 tryexec(cmdname, argv, envp);
6694 if (errno != ENOENT && errno != ENOTDIR)
6695 e = errno;
6696 }
6697 stunalloc(cmdname);
6698 }
6699 }
6700
6701 /* Map to POSIX errors */
6702 switch (e) {
6703 case EACCES:
6704 exerrno = 126;
6705 break;
6706 case ENOENT:
6707 exerrno = 127;
6708 break;
6709 default:
6710 exerrno = 2;
6711 break;
6712 }
6713 exitstatus = exerrno;
6714 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6715 argv[0], e, suppressint ));
6716 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6717 /* NOTREACHED */
6718}
6719
6720static void
6721printentry(struct tblentry *cmdp)
6722{
6723 int idx;
6724 const char *path;
6725 char *name;
6726
6727 idx = cmdp->param.index;
6728 path = pathval();
6729 do {
6730 name = padvance(&path, cmdp->cmdname);
6731 stunalloc(name);
6732 } while (--idx >= 0);
6733 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6734}
6735
6736/*
6737 * Clear out command entries. The argument specifies the first entry in
6738 * PATH which has changed.
6739 */
6740static void
6741clearcmdentry(int firstchange)
6742{
6743 struct tblentry **tblp;
6744 struct tblentry **pp;
6745 struct tblentry *cmdp;
6746
6747 INT_OFF;
6748 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6749 pp = tblp;
6750 while ((cmdp = *pp) != NULL) {
6751 if ((cmdp->cmdtype == CMDNORMAL &&
6752 cmdp->param.index >= firstchange)
6753 || (cmdp->cmdtype == CMDBUILTIN &&
6754 builtinloc >= firstchange)
6755 ) {
6756 *pp = cmdp->next;
6757 free(cmdp);
6758 } else {
6759 pp = &cmdp->next;
6760 }
6761 }
6762 }
6763 INT_ON;
6764}
6765
6766/*
6767 * Locate a command in the command hash table. If "add" is nonzero,
6768 * add the command to the table if it is not already present. The
6769 * variable "lastcmdentry" is set to point to the address of the link
6770 * pointing to the entry, so that delete_cmd_entry can delete the
6771 * entry.
6772 *
6773 * Interrupts must be off if called with add != 0.
6774 */
6775static struct tblentry **lastcmdentry;
6776
6777static struct tblentry *
6778cmdlookup(const char *name, int add)
6779{
6780 unsigned int hashval;
6781 const char *p;
6782 struct tblentry *cmdp;
6783 struct tblentry **pp;
6784
6785 p = name;
6786 hashval = (unsigned char)*p << 4;
6787 while (*p)
6788 hashval += (unsigned char)*p++;
6789 hashval &= 0x7FFF;
6790 pp = &cmdtable[hashval % CMDTABLESIZE];
6791 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6792 if (strcmp(cmdp->cmdname, name) == 0)
6793 break;
6794 pp = &cmdp->next;
6795 }
6796 if (add && cmdp == NULL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00006797 cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006798 + strlen(name) + 1);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006799 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006800 cmdp->cmdtype = CMDUNKNOWN;
6801 strcpy(cmdp->cmdname, name);
6802 }
6803 lastcmdentry = pp;
6804 return cmdp;
6805}
6806
6807/*
6808 * Delete the command entry returned on the last lookup.
6809 */
6810static void
6811delete_cmd_entry(void)
6812{
6813 struct tblentry *cmdp;
6814
6815 INT_OFF;
6816 cmdp = *lastcmdentry;
6817 *lastcmdentry = cmdp->next;
6818 if (cmdp->cmdtype == CMDFUNCTION)
6819 freefunc(cmdp->param.func);
6820 free(cmdp);
6821 INT_ON;
6822}
6823
6824/*
6825 * Add a new command entry, replacing any existing command entry for
6826 * the same name - except special builtins.
6827 */
6828static void
6829addcmdentry(char *name, struct cmdentry *entry)
6830{
6831 struct tblentry *cmdp;
6832
6833 cmdp = cmdlookup(name, 1);
6834 if (cmdp->cmdtype == CMDFUNCTION) {
6835 freefunc(cmdp->param.func);
6836 }
6837 cmdp->cmdtype = entry->cmdtype;
6838 cmdp->param = entry->u;
6839 cmdp->rehash = 0;
6840}
6841
6842static int
6843hashcmd(int argc, char **argv)
6844{
6845 struct tblentry **pp;
6846 struct tblentry *cmdp;
6847 int c;
6848 struct cmdentry entry;
6849 char *name;
6850
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006851 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006852 clearcmdentry(0);
6853 return 0;
6854 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006855
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006856 if (*argptr == NULL) {
6857 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6858 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6859 if (cmdp->cmdtype == CMDNORMAL)
6860 printentry(cmdp);
6861 }
6862 }
6863 return 0;
6864 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006865
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006866 c = 0;
6867 while ((name = *argptr) != NULL) {
6868 cmdp = cmdlookup(name, 0);
6869 if (cmdp != NULL
6870 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006871 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6872 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006873 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006874 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006875 find_command(name, &entry, DO_ERR, pathval());
6876 if (entry.cmdtype == CMDUNKNOWN)
6877 c = 1;
6878 argptr++;
6879 }
6880 return c;
6881}
6882
6883/*
6884 * Called when a cd is done. Marks all commands so the next time they
6885 * are executed they will be rehashed.
6886 */
6887static void
6888hashcd(void)
6889{
6890 struct tblentry **pp;
6891 struct tblentry *cmdp;
6892
6893 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6894 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006895 if (cmdp->cmdtype == CMDNORMAL
6896 || (cmdp->cmdtype == CMDBUILTIN
6897 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6898 && builtinloc > 0)
6899 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006901 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006902 }
6903 }
6904}
6905
6906/*
6907 * Fix command hash table when PATH changed.
6908 * Called before PATH is changed. The argument is the new value of PATH;
6909 * pathval() still returns the old value at this point.
6910 * Called with interrupts off.
6911 */
6912static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006913changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006914{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006915 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006916 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006917 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006918 int idx_bltin;
6919
6920 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006921 firstchange = 9999; /* assume no change */
6922 idx = 0;
6923 idx_bltin = -1;
6924 for (;;) {
6925 if (*old != *new) {
6926 firstchange = idx;
6927 if ((*old == '\0' && *new == ':')
6928 || (*old == ':' && *new == '\0'))
6929 firstchange++;
6930 old = new; /* ignore subsequent differences */
6931 }
6932 if (*new == '\0')
6933 break;
6934 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6935 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006936 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006937 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006938 new++, old++;
6939 }
6940 if (builtinloc < 0 && idx_bltin >= 0)
6941 builtinloc = idx_bltin; /* zap builtins */
6942 if (builtinloc >= 0 && idx_bltin < 0)
6943 firstchange = 0;
6944 clearcmdentry(firstchange);
6945 builtinloc = idx_bltin;
6946}
6947
6948#define TEOF 0
6949#define TNL 1
6950#define TREDIR 2
6951#define TWORD 3
6952#define TSEMI 4
6953#define TBACKGND 5
6954#define TAND 6
6955#define TOR 7
6956#define TPIPE 8
6957#define TLP 9
6958#define TRP 10
6959#define TENDCASE 11
6960#define TENDBQUOTE 12
6961#define TNOT 13
6962#define TCASE 14
6963#define TDO 15
6964#define TDONE 16
6965#define TELIF 17
6966#define TELSE 18
6967#define TESAC 19
6968#define TFI 20
6969#define TFOR 21
6970#define TIF 22
6971#define TIN 23
6972#define TTHEN 24
6973#define TUNTIL 25
6974#define TWHILE 26
6975#define TBEGIN 27
6976#define TEND 28
6977
6978/* first char is indicating which tokens mark the end of a list */
6979static const char *const tokname_array[] = {
6980 "\1end of file",
6981 "\0newline",
6982 "\0redirection",
6983 "\0word",
6984 "\0;",
6985 "\0&",
6986 "\0&&",
6987 "\0||",
6988 "\0|",
6989 "\0(",
6990 "\1)",
6991 "\1;;",
6992 "\1`",
6993#define KWDOFFSET 13
6994 /* the following are keywords */
6995 "\0!",
6996 "\0case",
6997 "\1do",
6998 "\1done",
6999 "\1elif",
7000 "\1else",
7001 "\1esac",
7002 "\1fi",
7003 "\0for",
7004 "\0if",
7005 "\0in",
7006 "\1then",
7007 "\0until",
7008 "\0while",
7009 "\0{",
7010 "\1}",
7011};
7012
7013static const char *
7014tokname(int tok)
7015{
7016 static char buf[16];
7017
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007018//try this:
7019//if (tok < TSEMI) return tokname_array[tok] + 1;
7020//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7021//return buf;
7022
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007023 if (tok >= TSEMI)
7024 buf[0] = '"';
7025 sprintf(buf + (tok >= TSEMI), "%s%c",
7026 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7027 return buf;
7028}
7029
7030/* Wrapper around strcmp for qsort/bsearch/... */
7031static int
7032pstrcmp(const void *a, const void *b)
7033{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007034 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007035}
7036
7037static const char *const *
7038findkwd(const char *s)
7039{
7040 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007041 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7042 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007043}
7044
7045/*
7046 * Locate and print what a word is...
7047 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007048static int
7049describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007050{
7051 struct cmdentry entry;
7052 struct tblentry *cmdp;
7053#if ENABLE_ASH_ALIAS
7054 const struct alias *ap;
7055#endif
7056 const char *path = pathval();
7057
7058 if (describe_command_verbose) {
7059 out1str(command);
7060 }
7061
7062 /* First look at the keywords */
7063 if (findkwd(command)) {
7064 out1str(describe_command_verbose ? " is a shell keyword" : command);
7065 goto out;
7066 }
7067
7068#if ENABLE_ASH_ALIAS
7069 /* Then look at the aliases */
7070 ap = lookupalias(command, 0);
7071 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007072 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007073 out1str("alias ");
7074 printalias(ap);
7075 return 0;
7076 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007077 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007078 goto out;
7079 }
7080#endif
7081 /* Then check if it is a tracked alias */
7082 cmdp = cmdlookup(command, 0);
7083 if (cmdp != NULL) {
7084 entry.cmdtype = cmdp->cmdtype;
7085 entry.u = cmdp->param;
7086 } else {
7087 /* Finally use brute force */
7088 find_command(command, &entry, DO_ABS, path);
7089 }
7090
7091 switch (entry.cmdtype) {
7092 case CMDNORMAL: {
7093 int j = entry.u.index;
7094 char *p;
7095 if (j == -1) {
7096 p = command;
7097 } else {
7098 do {
7099 p = padvance(&path, command);
7100 stunalloc(p);
7101 } while (--j >= 0);
7102 }
7103 if (describe_command_verbose) {
7104 out1fmt(" is%s %s",
7105 (cmdp ? " a tracked alias for" : nullstr), p
7106 );
7107 } else {
7108 out1str(p);
7109 }
7110 break;
7111 }
7112
7113 case CMDFUNCTION:
7114 if (describe_command_verbose) {
7115 out1str(" is a shell function");
7116 } else {
7117 out1str(command);
7118 }
7119 break;
7120
7121 case CMDBUILTIN:
7122 if (describe_command_verbose) {
7123 out1fmt(" is a %sshell builtin",
7124 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7125 "special " : nullstr
7126 );
7127 } else {
7128 out1str(command);
7129 }
7130 break;
7131
7132 default:
7133 if (describe_command_verbose) {
7134 out1str(": not found\n");
7135 }
7136 return 127;
7137 }
7138 out:
7139 outstr("\n", stdout);
7140 return 0;
7141}
7142
7143static int
7144typecmd(int argc, char **argv)
7145{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007146 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007147 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007148 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007149
Denis Vlasenko46846e22007-05-20 13:08:31 +00007150 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007151 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007152 i++;
7153 verbose = 0;
7154 }
7155 while (i < argc) {
7156 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007157 }
7158 return err;
7159}
7160
7161#if ENABLE_ASH_CMDCMD
7162static int
7163commandcmd(int argc, char **argv)
7164{
7165 int c;
7166 enum {
7167 VERIFY_BRIEF = 1,
7168 VERIFY_VERBOSE = 2,
7169 } verify = 0;
7170
7171 while ((c = nextopt("pvV")) != '\0')
7172 if (c == 'V')
7173 verify |= VERIFY_VERBOSE;
7174 else if (c == 'v')
7175 verify |= VERIFY_BRIEF;
7176#if DEBUG
7177 else if (c != 'p')
7178 abort();
7179#endif
7180 if (verify)
7181 return describe_command(*argptr, verify - VERIFY_BRIEF);
7182
7183 return 0;
7184}
7185#endif
7186
7187
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007188/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007189
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007190static int funcblocksize; /* size of structures in function */
7191static int funcstringsize; /* size of strings in node */
7192static void *funcblock; /* block to allocate function from */
7193static char *funcstring; /* block to allocate strings from */
7194
Eric Andersencb57d552001-06-28 07:25:16 +00007195/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007196#define EV_EXIT 01 /* exit after evaluating tree */
7197#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7198#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007199
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007200static const short nodesize[26] = {
7201 SHELL_ALIGN(sizeof(struct ncmd)),
7202 SHELL_ALIGN(sizeof(struct npipe)),
7203 SHELL_ALIGN(sizeof(struct nredir)),
7204 SHELL_ALIGN(sizeof(struct nredir)),
7205 SHELL_ALIGN(sizeof(struct nredir)),
7206 SHELL_ALIGN(sizeof(struct nbinary)),
7207 SHELL_ALIGN(sizeof(struct nbinary)),
7208 SHELL_ALIGN(sizeof(struct nbinary)),
7209 SHELL_ALIGN(sizeof(struct nif)),
7210 SHELL_ALIGN(sizeof(struct nbinary)),
7211 SHELL_ALIGN(sizeof(struct nbinary)),
7212 SHELL_ALIGN(sizeof(struct nfor)),
7213 SHELL_ALIGN(sizeof(struct ncase)),
7214 SHELL_ALIGN(sizeof(struct nclist)),
7215 SHELL_ALIGN(sizeof(struct narg)),
7216 SHELL_ALIGN(sizeof(struct narg)),
7217 SHELL_ALIGN(sizeof(struct nfile)),
7218 SHELL_ALIGN(sizeof(struct nfile)),
7219 SHELL_ALIGN(sizeof(struct nfile)),
7220 SHELL_ALIGN(sizeof(struct nfile)),
7221 SHELL_ALIGN(sizeof(struct nfile)),
7222 SHELL_ALIGN(sizeof(struct ndup)),
7223 SHELL_ALIGN(sizeof(struct ndup)),
7224 SHELL_ALIGN(sizeof(struct nhere)),
7225 SHELL_ALIGN(sizeof(struct nhere)),
7226 SHELL_ALIGN(sizeof(struct nnot)),
7227};
7228
7229static void calcsize(union node *n);
7230
7231static void
7232sizenodelist(struct nodelist *lp)
7233{
7234 while (lp) {
7235 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7236 calcsize(lp->n);
7237 lp = lp->next;
7238 }
7239}
7240
7241static void
7242calcsize(union node *n)
7243{
7244 if (n == NULL)
7245 return;
7246 funcblocksize += nodesize[n->type];
7247 switch (n->type) {
7248 case NCMD:
7249 calcsize(n->ncmd.redirect);
7250 calcsize(n->ncmd.args);
7251 calcsize(n->ncmd.assign);
7252 break;
7253 case NPIPE:
7254 sizenodelist(n->npipe.cmdlist);
7255 break;
7256 case NREDIR:
7257 case NBACKGND:
7258 case NSUBSHELL:
7259 calcsize(n->nredir.redirect);
7260 calcsize(n->nredir.n);
7261 break;
7262 case NAND:
7263 case NOR:
7264 case NSEMI:
7265 case NWHILE:
7266 case NUNTIL:
7267 calcsize(n->nbinary.ch2);
7268 calcsize(n->nbinary.ch1);
7269 break;
7270 case NIF:
7271 calcsize(n->nif.elsepart);
7272 calcsize(n->nif.ifpart);
7273 calcsize(n->nif.test);
7274 break;
7275 case NFOR:
7276 funcstringsize += strlen(n->nfor.var) + 1;
7277 calcsize(n->nfor.body);
7278 calcsize(n->nfor.args);
7279 break;
7280 case NCASE:
7281 calcsize(n->ncase.cases);
7282 calcsize(n->ncase.expr);
7283 break;
7284 case NCLIST:
7285 calcsize(n->nclist.body);
7286 calcsize(n->nclist.pattern);
7287 calcsize(n->nclist.next);
7288 break;
7289 case NDEFUN:
7290 case NARG:
7291 sizenodelist(n->narg.backquote);
7292 funcstringsize += strlen(n->narg.text) + 1;
7293 calcsize(n->narg.next);
7294 break;
7295 case NTO:
7296 case NCLOBBER:
7297 case NFROM:
7298 case NFROMTO:
7299 case NAPPEND:
7300 calcsize(n->nfile.fname);
7301 calcsize(n->nfile.next);
7302 break;
7303 case NTOFD:
7304 case NFROMFD:
7305 calcsize(n->ndup.vname);
7306 calcsize(n->ndup.next);
7307 break;
7308 case NHERE:
7309 case NXHERE:
7310 calcsize(n->nhere.doc);
7311 calcsize(n->nhere.next);
7312 break;
7313 case NNOT:
7314 calcsize(n->nnot.com);
7315 break;
7316 };
7317}
7318
7319static char *
7320nodeckstrdup(char *s)
7321{
7322 char *rtn = funcstring;
7323
7324 strcpy(funcstring, s);
7325 funcstring += strlen(s) + 1;
7326 return rtn;
7327}
7328
7329static union node *copynode(union node *);
7330
7331static struct nodelist *
7332copynodelist(struct nodelist *lp)
7333{
7334 struct nodelist *start;
7335 struct nodelist **lpp;
7336
7337 lpp = &start;
7338 while (lp) {
7339 *lpp = funcblock;
7340 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7341 (*lpp)->n = copynode(lp->n);
7342 lp = lp->next;
7343 lpp = &(*lpp)->next;
7344 }
7345 *lpp = NULL;
7346 return start;
7347}
7348
7349static union node *
7350copynode(union node *n)
7351{
7352 union node *new;
7353
7354 if (n == NULL)
7355 return NULL;
7356 new = funcblock;
7357 funcblock = (char *) funcblock + nodesize[n->type];
7358
7359 switch (n->type) {
7360 case NCMD:
7361 new->ncmd.redirect = copynode(n->ncmd.redirect);
7362 new->ncmd.args = copynode(n->ncmd.args);
7363 new->ncmd.assign = copynode(n->ncmd.assign);
7364 break;
7365 case NPIPE:
7366 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7367 new->npipe.backgnd = n->npipe.backgnd;
7368 break;
7369 case NREDIR:
7370 case NBACKGND:
7371 case NSUBSHELL:
7372 new->nredir.redirect = copynode(n->nredir.redirect);
7373 new->nredir.n = copynode(n->nredir.n);
7374 break;
7375 case NAND:
7376 case NOR:
7377 case NSEMI:
7378 case NWHILE:
7379 case NUNTIL:
7380 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7381 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7382 break;
7383 case NIF:
7384 new->nif.elsepart = copynode(n->nif.elsepart);
7385 new->nif.ifpart = copynode(n->nif.ifpart);
7386 new->nif.test = copynode(n->nif.test);
7387 break;
7388 case NFOR:
7389 new->nfor.var = nodeckstrdup(n->nfor.var);
7390 new->nfor.body = copynode(n->nfor.body);
7391 new->nfor.args = copynode(n->nfor.args);
7392 break;
7393 case NCASE:
7394 new->ncase.cases = copynode(n->ncase.cases);
7395 new->ncase.expr = copynode(n->ncase.expr);
7396 break;
7397 case NCLIST:
7398 new->nclist.body = copynode(n->nclist.body);
7399 new->nclist.pattern = copynode(n->nclist.pattern);
7400 new->nclist.next = copynode(n->nclist.next);
7401 break;
7402 case NDEFUN:
7403 case NARG:
7404 new->narg.backquote = copynodelist(n->narg.backquote);
7405 new->narg.text = nodeckstrdup(n->narg.text);
7406 new->narg.next = copynode(n->narg.next);
7407 break;
7408 case NTO:
7409 case NCLOBBER:
7410 case NFROM:
7411 case NFROMTO:
7412 case NAPPEND:
7413 new->nfile.fname = copynode(n->nfile.fname);
7414 new->nfile.fd = n->nfile.fd;
7415 new->nfile.next = copynode(n->nfile.next);
7416 break;
7417 case NTOFD:
7418 case NFROMFD:
7419 new->ndup.vname = copynode(n->ndup.vname);
7420 new->ndup.dupfd = n->ndup.dupfd;
7421 new->ndup.fd = n->ndup.fd;
7422 new->ndup.next = copynode(n->ndup.next);
7423 break;
7424 case NHERE:
7425 case NXHERE:
7426 new->nhere.doc = copynode(n->nhere.doc);
7427 new->nhere.fd = n->nhere.fd;
7428 new->nhere.next = copynode(n->nhere.next);
7429 break;
7430 case NNOT:
7431 new->nnot.com = copynode(n->nnot.com);
7432 break;
7433 };
7434 new->type = n->type;
7435 return new;
7436}
7437
7438/*
7439 * Make a copy of a parse tree.
7440 */
7441static struct funcnode *
7442copyfunc(union node *n)
7443{
7444 struct funcnode *f;
7445 size_t blocksize;
7446
7447 funcblocksize = offsetof(struct funcnode, n);
7448 funcstringsize = 0;
7449 calcsize(n);
7450 blocksize = funcblocksize;
7451 f = ckmalloc(blocksize + funcstringsize);
7452 funcblock = (char *) f + offsetof(struct funcnode, n);
7453 funcstring = (char *) f + blocksize;
7454 copynode(n);
7455 f->count = 0;
7456 return f;
7457}
7458
7459/*
7460 * Define a shell function.
7461 */
7462static void
7463defun(char *name, union node *func)
7464{
7465 struct cmdentry entry;
7466
7467 INT_OFF;
7468 entry.cmdtype = CMDFUNCTION;
7469 entry.u.func = copyfunc(func);
7470 addcmdentry(name, &entry);
7471 INT_ON;
7472}
7473
7474static int evalskip; /* set if we are skipping commands */
7475/* reasons for skipping commands (see comment on breakcmd routine) */
7476#define SKIPBREAK (1 << 0)
7477#define SKIPCONT (1 << 1)
7478#define SKIPFUNC (1 << 2)
7479#define SKIPFILE (1 << 3)
7480#define SKIPEVAL (1 << 4)
7481static int skipcount; /* number of levels to skip */
7482static int funcnest; /* depth of function calls */
7483
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007484/* forward decl way out to parsing code - dotrap needs it */
7485static int evalstring(char *s, int mask);
7486
7487/*
7488 * Called to execute a trap. Perhaps we should avoid entering new trap
7489 * handlers while we are executing a trap handler.
7490 */
7491static int
7492dotrap(void)
7493{
7494 char *p;
7495 char *q;
7496 int i;
7497 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007498 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007499
7500 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007501 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007502 xbarrier();
7503
7504 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7505 if (!*q)
7506 continue;
7507 *q = '\0';
7508
7509 p = trap[i + 1];
7510 if (!p)
7511 continue;
7512 skip = evalstring(p, SKIPEVAL);
7513 exitstatus = savestatus;
7514 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007515 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007516 }
7517
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007518 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007519}
7520
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007521/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007522static void evalloop(union node *, int);
7523static void evalfor(union node *, int);
7524static void evalcase(union node *, int);
7525static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007526static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007527static void evalpipe(union node *, int);
7528static void evalcommand(union node *, int);
7529static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007530static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007531
Eric Andersen62483552001-07-10 06:09:16 +00007532/*
Eric Andersenc470f442003-07-28 09:56:35 +00007533 * Evaluate a parse tree. The value is left in the global variable
7534 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007535 */
Eric Andersenc470f442003-07-28 09:56:35 +00007536static void
7537evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007538{
Eric Andersenc470f442003-07-28 09:56:35 +00007539 int checkexit = 0;
7540 void (*evalfn)(union node *, int);
7541 unsigned isor;
7542 int status;
7543 if (n == NULL) {
7544 TRACE(("evaltree(NULL) called\n"));
7545 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007546 }
Eric Andersenc470f442003-07-28 09:56:35 +00007547 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007548 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007549 switch (n->type) {
7550 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007551#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007552 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007553 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007554 break;
7555#endif
7556 case NNOT:
7557 evaltree(n->nnot.com, EV_TESTED);
7558 status = !exitstatus;
7559 goto setstatus;
7560 case NREDIR:
7561 expredir(n->nredir.redirect);
7562 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7563 if (!status) {
7564 evaltree(n->nredir.n, flags & EV_TESTED);
7565 status = exitstatus;
7566 }
7567 popredir(0);
7568 goto setstatus;
7569 case NCMD:
7570 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007571 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007572 if (eflag && !(flags & EV_TESTED))
7573 checkexit = ~0;
7574 goto calleval;
7575 case NFOR:
7576 evalfn = evalfor;
7577 goto calleval;
7578 case NWHILE:
7579 case NUNTIL:
7580 evalfn = evalloop;
7581 goto calleval;
7582 case NSUBSHELL:
7583 case NBACKGND:
7584 evalfn = evalsubshell;
7585 goto calleval;
7586 case NPIPE:
7587 evalfn = evalpipe;
7588 goto checkexit;
7589 case NCASE:
7590 evalfn = evalcase;
7591 goto calleval;
7592 case NAND:
7593 case NOR:
7594 case NSEMI:
7595#if NAND + 1 != NOR
7596#error NAND + 1 != NOR
7597#endif
7598#if NOR + 1 != NSEMI
7599#error NOR + 1 != NSEMI
7600#endif
7601 isor = n->type - NAND;
7602 evaltree(
7603 n->nbinary.ch1,
7604 (flags | ((isor >> 1) - 1)) & EV_TESTED
7605 );
7606 if (!exitstatus == isor)
7607 break;
7608 if (!evalskip) {
7609 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007610 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007611 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007612 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007613 evalfn(n, flags);
7614 break;
7615 }
7616 break;
7617 case NIF:
7618 evaltree(n->nif.test, EV_TESTED);
7619 if (evalskip)
7620 break;
7621 if (exitstatus == 0) {
7622 n = n->nif.ifpart;
7623 goto evaln;
7624 } else if (n->nif.elsepart) {
7625 n = n->nif.elsepart;
7626 goto evaln;
7627 }
7628 goto success;
7629 case NDEFUN:
7630 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007631 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007632 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007633 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007634 exitstatus = status;
7635 break;
7636 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007637 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007638 if ((checkexit & exitstatus))
7639 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007640 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007641 goto exexit;
7642
7643 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007644 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007645 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007646 }
Eric Andersen62483552001-07-10 06:09:16 +00007647}
7648
Eric Andersenc470f442003-07-28 09:56:35 +00007649#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7650static
7651#endif
7652void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7653
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007654static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007655
7656static void
7657evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007658{
7659 int status;
7660
7661 loopnest++;
7662 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007663 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007664 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007665 int i;
7666
Eric Andersencb57d552001-06-28 07:25:16 +00007667 evaltree(n->nbinary.ch1, EV_TESTED);
7668 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007669 skipping:
7670 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007671 evalskip = 0;
7672 continue;
7673 }
7674 if (evalskip == SKIPBREAK && --skipcount <= 0)
7675 evalskip = 0;
7676 break;
7677 }
Eric Andersenc470f442003-07-28 09:56:35 +00007678 i = exitstatus;
7679 if (n->type != NWHILE)
7680 i = !i;
7681 if (i != 0)
7682 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007683 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007684 status = exitstatus;
7685 if (evalskip)
7686 goto skipping;
7687 }
7688 loopnest--;
7689 exitstatus = status;
7690}
7691
Eric Andersenc470f442003-07-28 09:56:35 +00007692static void
7693evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007694{
7695 struct arglist arglist;
7696 union node *argp;
7697 struct strlist *sp;
7698 struct stackmark smark;
7699
7700 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007701 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007702 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007703 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007704 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007705 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007706 if (evalskip)
7707 goto out;
7708 }
7709 *arglist.lastp = NULL;
7710
7711 exitstatus = 0;
7712 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007713 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007714 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007715 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007716 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007717 if (evalskip) {
7718 if (evalskip == SKIPCONT && --skipcount <= 0) {
7719 evalskip = 0;
7720 continue;
7721 }
7722 if (evalskip == SKIPBREAK && --skipcount <= 0)
7723 evalskip = 0;
7724 break;
7725 }
7726 }
7727 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007728 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007729 popstackmark(&smark);
7730}
7731
Eric Andersenc470f442003-07-28 09:56:35 +00007732static void
7733evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007734{
7735 union node *cp;
7736 union node *patp;
7737 struct arglist arglist;
7738 struct stackmark smark;
7739
7740 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007741 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007742 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007743 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007744 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007745 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7746 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007747 if (casematch(patp, arglist.list->text)) {
7748 if (evalskip == 0) {
7749 evaltree(cp->nclist.body, flags);
7750 }
7751 goto out;
7752 }
7753 }
7754 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007755 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007756 popstackmark(&smark);
7757}
7758
Eric Andersenc470f442003-07-28 09:56:35 +00007759/*
7760 * Kick off a subshell to evaluate a tree.
7761 */
Eric Andersenc470f442003-07-28 09:56:35 +00007762static void
7763evalsubshell(union node *n, int flags)
7764{
7765 struct job *jp;
7766 int backgnd = (n->type == NBACKGND);
7767 int status;
7768
7769 expredir(n->nredir.redirect);
7770 if (!backgnd && flags & EV_EXIT && !trap[0])
7771 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007772 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007773 jp = makejob(n, 1);
7774 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007775 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007776 flags |= EV_EXIT;
7777 if (backgnd)
7778 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007779 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007780 redirect(n->nredir.redirect, 0);
7781 evaltreenr(n->nredir.n, flags);
7782 /* never returns */
7783 }
7784 status = 0;
7785 if (! backgnd)
7786 status = waitforjob(jp);
7787 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007788 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007789}
7790
Eric Andersenc470f442003-07-28 09:56:35 +00007791/*
7792 * Compute the names of the files in a redirection list.
7793 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007794static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007795static void
7796expredir(union node *n)
7797{
7798 union node *redir;
7799
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007800 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007801 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007802
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007803 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00007804 fn.lastp = &fn.list;
7805 switch (redir->type) {
7806 case NFROMTO:
7807 case NFROM:
7808 case NTO:
7809 case NCLOBBER:
7810 case NAPPEND:
7811 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7812 redir->nfile.expfname = fn.list->text;
7813 break;
7814 case NFROMFD:
7815 case NTOFD:
7816 if (redir->ndup.vname) {
7817 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007818 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007819 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007820 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007821 }
7822 break;
7823 }
7824 }
7825}
7826
Eric Andersencb57d552001-06-28 07:25:16 +00007827/*
Eric Andersencb57d552001-06-28 07:25:16 +00007828 * Evaluate a pipeline. All the processes in the pipeline are children
7829 * of the process creating the pipeline. (This differs from some versions
7830 * of the shell, which make the last process in a pipeline the parent
7831 * of all the rest.)
7832 */
Eric Andersenc470f442003-07-28 09:56:35 +00007833static void
7834evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007835{
7836 struct job *jp;
7837 struct nodelist *lp;
7838 int pipelen;
7839 int prevfd;
7840 int pip[2];
7841
Eric Andersenc470f442003-07-28 09:56:35 +00007842 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007843 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007844 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007845 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007846 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007847 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007848 jp = makejob(n, pipelen);
7849 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007850 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007851 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007852 pip[1] = -1;
7853 if (lp->next) {
7854 if (pipe(pip) < 0) {
7855 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007856 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007857 }
7858 }
7859 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007860 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007861 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007862 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007863 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007864 if (prevfd > 0) {
7865 dup2(prevfd, 0);
7866 close(prevfd);
7867 }
7868 if (pip[1] > 1) {
7869 dup2(pip[1], 1);
7870 close(pip[1]);
7871 }
Eric Andersenc470f442003-07-28 09:56:35 +00007872 evaltreenr(lp->n, flags);
7873 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007874 }
7875 if (prevfd >= 0)
7876 close(prevfd);
7877 prevfd = pip[0];
7878 close(pip[1]);
7879 }
Eric Andersencb57d552001-06-28 07:25:16 +00007880 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007881 exitstatus = waitforjob(jp);
7882 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007883 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007884 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007885}
7886
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007887/*
7888 * Controls whether the shell is interactive or not.
7889 */
7890static void
7891setinteractive(int on)
7892{
7893 static int is_interactive;
7894
7895 if (++on == is_interactive)
7896 return;
7897 is_interactive = on;
7898 setsignal(SIGINT);
7899 setsignal(SIGQUIT);
7900 setsignal(SIGTERM);
7901#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7902 if (is_interactive > 1) {
7903 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007904 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007905
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007906 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007907 out1fmt(
7908 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007909 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007910 "Enter 'help' for a list of built-in commands."
7911 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007912 bb_banner);
7913 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007914 }
7915 }
7916#endif
7917}
7918
7919#if ENABLE_FEATURE_EDITING_VI
7920#define setvimode(on) do { \
7921 if (on) line_input_state->flags |= VI_MODE; \
7922 else line_input_state->flags &= ~VI_MODE; \
7923} while (0)
7924#else
7925#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7926#endif
7927
7928static void
7929optschanged(void)
7930{
7931#if DEBUG
7932 opentrace();
7933#endif
7934 setinteractive(iflag);
7935 setjobctl(mflag);
7936 setvimode(viflag);
7937}
7938
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007939static struct localvar *localvars;
7940
7941/*
7942 * Called after a function returns.
7943 * Interrupts must be off.
7944 */
7945static void
7946poplocalvars(void)
7947{
7948 struct localvar *lvp;
7949 struct var *vp;
7950
7951 while ((lvp = localvars) != NULL) {
7952 localvars = lvp->next;
7953 vp = lvp->vp;
7954 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7955 if (vp == NULL) { /* $- saved */
7956 memcpy(optlist, lvp->text, sizeof(optlist));
7957 free((char*)lvp->text);
7958 optschanged();
7959 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7960 unsetvar(vp->text);
7961 } else {
7962 if (vp->func)
7963 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7964 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7965 free((char*)vp->text);
7966 vp->flags = lvp->flags;
7967 vp->text = lvp->text;
7968 }
7969 free(lvp);
7970 }
7971}
7972
7973static int
7974evalfun(struct funcnode *func, int argc, char **argv, int flags)
7975{
7976 volatile struct shparam saveparam;
7977 struct localvar *volatile savelocalvars;
7978 struct jmploc *volatile savehandler;
7979 struct jmploc jmploc;
7980 int e;
7981
7982 saveparam = shellparam;
7983 savelocalvars = localvars;
7984 e = setjmp(jmploc.loc);
7985 if (e) {
7986 goto funcdone;
7987 }
7988 INT_OFF;
7989 savehandler = exception_handler;
7990 exception_handler = &jmploc;
7991 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007992 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007993 func->count++;
7994 funcnest++;
7995 INT_ON;
7996 shellparam.nparam = argc - 1;
7997 shellparam.p = argv + 1;
7998#if ENABLE_ASH_GETOPTS
7999 shellparam.optind = 1;
8000 shellparam.optoff = -1;
8001#endif
8002 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008003 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008004 INT_OFF;
8005 funcnest--;
8006 freefunc(func);
8007 poplocalvars();
8008 localvars = savelocalvars;
8009 freeparam(&shellparam);
8010 shellparam = saveparam;
8011 exception_handler = savehandler;
8012 INT_ON;
8013 evalskip &= ~SKIPFUNC;
8014 return e;
8015}
8016
Denis Vlasenko131ae172007-02-18 13:00:19 +00008017#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008018static char **
8019parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008020{
8021 char *cp, c;
8022
8023 for (;;) {
8024 cp = *++argv;
8025 if (!cp)
8026 return 0;
8027 if (*cp++ != '-')
8028 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008029 c = *cp++;
8030 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008031 break;
8032 if (c == '-' && !*cp) {
8033 argv++;
8034 break;
8035 }
8036 do {
8037 switch (c) {
8038 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008039 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008040 break;
8041 default:
8042 /* run 'typecmd' for other options */
8043 return 0;
8044 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008045 c = *cp++;
8046 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008047 }
8048 return argv;
8049}
8050#endif
8051
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008052/*
8053 * Make a variable a local variable. When a variable is made local, it's
8054 * value and flags are saved in a localvar structure. The saved values
8055 * will be restored when the shell function returns. We handle the name
8056 * "-" as a special case.
8057 */
8058static void
8059mklocal(char *name)
8060{
8061 struct localvar *lvp;
8062 struct var **vpp;
8063 struct var *vp;
8064
8065 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008066 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008067 if (LONE_DASH(name)) {
8068 char *p;
8069 p = ckmalloc(sizeof(optlist));
8070 lvp->text = memcpy(p, optlist, sizeof(optlist));
8071 vp = NULL;
8072 } else {
8073 char *eq;
8074
8075 vpp = hashvar(name);
8076 vp = *findvar(vpp, name);
8077 eq = strchr(name, '=');
8078 if (vp == NULL) {
8079 if (eq)
8080 setvareq(name, VSTRFIXED);
8081 else
8082 setvar(name, NULL, VSTRFIXED);
8083 vp = *vpp; /* the new variable */
8084 lvp->flags = VUNSET;
8085 } else {
8086 lvp->text = vp->text;
8087 lvp->flags = vp->flags;
8088 vp->flags |= VSTRFIXED|VTEXTFIXED;
8089 if (eq)
8090 setvareq(name, 0);
8091 }
8092 }
8093 lvp->vp = vp;
8094 lvp->next = localvars;
8095 localvars = lvp;
8096 INT_ON;
8097}
8098
8099/*
8100 * The "local" command.
8101 */
8102static int
8103localcmd(int argc, char **argv)
8104{
8105 char *name;
8106
8107 argv = argptr;
8108 while ((name = *argv++) != NULL) {
8109 mklocal(name);
8110 }
8111 return 0;
8112}
8113
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008114static int
8115falsecmd(int argc, char **argv)
8116{
8117 return 1;
8118}
8119
8120static int
8121truecmd(int argc, char **argv)
8122{
8123 return 0;
8124}
8125
8126static int
8127execcmd(int argc, char **argv)
8128{
8129 if (argc > 1) {
8130 iflag = 0; /* exit on error */
8131 mflag = 0;
8132 optschanged();
8133 shellexec(argv + 1, pathval(), 0);
8134 }
8135 return 0;
8136}
8137
8138/*
8139 * The return command.
8140 */
8141static int
8142returncmd(int argc, char **argv)
8143{
8144 /*
8145 * If called outside a function, do what ksh does;
8146 * skip the rest of the file.
8147 */
8148 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8149 return argv[1] ? number(argv[1]) : exitstatus;
8150}
8151
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008152/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008153static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008154static int dotcmd(int, char **);
8155static int evalcmd(int, char **);
8156#if ENABLE_ASH_BUILTIN_ECHO
8157static int echocmd(int, char **);
8158#endif
8159#if ENABLE_ASH_BUILTIN_TEST
8160static int testcmd(int, char **);
8161#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008162static int exitcmd(int, char **);
8163static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008164#if ENABLE_ASH_GETOPTS
8165static int getoptscmd(int, char **);
8166#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008167#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8168static int helpcmd(int argc, char **argv);
8169#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008170#if ENABLE_ASH_MATH_SUPPORT
8171static int letcmd(int, char **);
8172#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008173static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008174static int setcmd(int, char **);
8175static int shiftcmd(int, char **);
8176static int timescmd(int, char **);
8177static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008178static int umaskcmd(int, char **);
8179static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008180static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008181
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008182#define BUILTIN_NOSPEC "0"
8183#define BUILTIN_SPECIAL "1"
8184#define BUILTIN_REGULAR "2"
8185#define BUILTIN_SPEC_REG "3"
8186#define BUILTIN_ASSIGN "4"
8187#define BUILTIN_SPEC_ASSG "5"
8188#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008189#define BUILTIN_SPEC_REG_ASSG "7"
8190
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008191/* make sure to keep these in proper order since it is searched via bsearch() */
8192static const struct builtincmd builtintab[] = {
8193 { BUILTIN_SPEC_REG ".", dotcmd },
8194 { BUILTIN_SPEC_REG ":", truecmd },
8195#if ENABLE_ASH_BUILTIN_TEST
8196 { BUILTIN_REGULAR "[", testcmd },
8197 { BUILTIN_REGULAR "[[", testcmd },
8198#endif
8199#if ENABLE_ASH_ALIAS
8200 { BUILTIN_REG_ASSG "alias", aliascmd },
8201#endif
8202#if JOBS
8203 { BUILTIN_REGULAR "bg", fg_bgcmd },
8204#endif
8205 { BUILTIN_SPEC_REG "break", breakcmd },
8206 { BUILTIN_REGULAR "cd", cdcmd },
8207 { BUILTIN_NOSPEC "chdir", cdcmd },
8208#if ENABLE_ASH_CMDCMD
8209 { BUILTIN_REGULAR "command", commandcmd },
8210#endif
8211 { BUILTIN_SPEC_REG "continue", breakcmd },
8212#if ENABLE_ASH_BUILTIN_ECHO
8213 { BUILTIN_REGULAR "echo", echocmd },
8214#endif
8215 { BUILTIN_SPEC_REG "eval", evalcmd },
8216 { BUILTIN_SPEC_REG "exec", execcmd },
8217 { BUILTIN_SPEC_REG "exit", exitcmd },
8218 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8219 { BUILTIN_REGULAR "false", falsecmd },
8220#if JOBS
8221 { BUILTIN_REGULAR "fg", fg_bgcmd },
8222#endif
8223#if ENABLE_ASH_GETOPTS
8224 { BUILTIN_REGULAR "getopts", getoptscmd },
8225#endif
8226 { BUILTIN_NOSPEC "hash", hashcmd },
8227#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8228 { BUILTIN_NOSPEC "help", helpcmd },
8229#endif
8230#if JOBS
8231 { BUILTIN_REGULAR "jobs", jobscmd },
8232 { BUILTIN_REGULAR "kill", killcmd },
8233#endif
8234#if ENABLE_ASH_MATH_SUPPORT
8235 { BUILTIN_NOSPEC "let", letcmd },
8236#endif
8237 { BUILTIN_ASSIGN "local", localcmd },
8238 { BUILTIN_NOSPEC "pwd", pwdcmd },
8239 { BUILTIN_REGULAR "read", readcmd },
8240 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8241 { BUILTIN_SPEC_REG "return", returncmd },
8242 { BUILTIN_SPEC_REG "set", setcmd },
8243 { BUILTIN_SPEC_REG "shift", shiftcmd },
8244 { BUILTIN_SPEC_REG "source", dotcmd },
8245#if ENABLE_ASH_BUILTIN_TEST
8246 { BUILTIN_REGULAR "test", testcmd },
8247#endif
8248 { BUILTIN_SPEC_REG "times", timescmd },
8249 { BUILTIN_SPEC_REG "trap", trapcmd },
8250 { BUILTIN_REGULAR "true", truecmd },
8251 { BUILTIN_NOSPEC "type", typecmd },
8252 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8253 { BUILTIN_REGULAR "umask", umaskcmd },
8254#if ENABLE_ASH_ALIAS
8255 { BUILTIN_REGULAR "unalias", unaliascmd },
8256#endif
8257 { BUILTIN_SPEC_REG "unset", unsetcmd },
8258 { BUILTIN_REGULAR "wait", waitcmd },
8259};
8260
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008261
8262#define COMMANDCMD (builtintab + 5 + \
8263 2 * ENABLE_ASH_BUILTIN_TEST + \
8264 ENABLE_ASH_ALIAS + \
8265 ENABLE_ASH_JOB_CONTROL)
8266#define EXECCMD (builtintab + 7 + \
8267 2 * ENABLE_ASH_BUILTIN_TEST + \
8268 ENABLE_ASH_ALIAS + \
8269 ENABLE_ASH_JOB_CONTROL + \
8270 ENABLE_ASH_CMDCMD + \
8271 ENABLE_ASH_BUILTIN_ECHO)
8272
8273/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008274 * Search the table of builtin commands.
8275 */
8276static struct builtincmd *
8277find_builtin(const char *name)
8278{
8279 struct builtincmd *bp;
8280
8281 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008282 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008283 pstrcmp
8284 );
8285 return bp;
8286}
8287
8288/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008289 * Execute a simple command.
8290 */
8291static int back_exitstatus; /* exit status of backquoted command */
8292static int
8293isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008294{
8295 const char *q = endofname(p);
8296 if (p == q)
8297 return 0;
8298 return *q == '=';
8299}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008300static int
8301bltincmd(int argc, char **argv)
8302{
8303 /* Preserve exitstatus of a previous possible redirection
8304 * as POSIX mandates */
8305 return back_exitstatus;
8306}
Eric Andersenc470f442003-07-28 09:56:35 +00008307static void
8308evalcommand(union node *cmd, int flags)
8309{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008310 static const struct builtincmd null_bltin = {
8311 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008312 };
Eric Andersenc470f442003-07-28 09:56:35 +00008313 struct stackmark smark;
8314 union node *argp;
8315 struct arglist arglist;
8316 struct arglist varlist;
8317 char **argv;
8318 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008319 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008320 struct cmdentry cmdentry;
8321 struct job *jp;
8322 char *lastarg;
8323 const char *path;
8324 int spclbltin;
8325 int cmd_is_exec;
8326 int status;
8327 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008328 struct builtincmd *bcmd;
8329 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008330
8331 /* First expand the arguments. */
8332 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8333 setstackmark(&smark);
8334 back_exitstatus = 0;
8335
8336 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008337 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008338 varlist.lastp = &varlist.list;
8339 *varlist.lastp = NULL;
8340 arglist.lastp = &arglist.list;
8341 *arglist.lastp = NULL;
8342
8343 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008344 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008345 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8346 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8347 }
8348
Eric Andersenc470f442003-07-28 09:56:35 +00008349 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8350 struct strlist **spp;
8351
8352 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008353 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008354 expandarg(argp, &arglist, EXP_VARTILDE);
8355 else
8356 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8357
Eric Andersenc470f442003-07-28 09:56:35 +00008358 for (sp = *spp; sp; sp = sp->next)
8359 argc++;
8360 }
8361
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008362 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008363 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008364 TRACE(("evalcommand arg: %s\n", sp->text));
8365 *nargv++ = sp->text;
8366 }
8367 *nargv = NULL;
8368
8369 lastarg = NULL;
8370 if (iflag && funcnest == 0 && argc > 0)
8371 lastarg = nargv[-1];
8372
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008373 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008374 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008375 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008376
8377 path = vpath.text;
8378 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8379 struct strlist **spp;
8380 char *p;
8381
8382 spp = varlist.lastp;
8383 expandarg(argp, &varlist, EXP_VARTILDE);
8384
8385 /*
8386 * Modify the command lookup path, if a PATH= assignment
8387 * is present
8388 */
8389 p = (*spp)->text;
8390 if (varequal(p, path))
8391 path = p;
8392 }
8393
8394 /* Print the command if xflag is set. */
8395 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008396 int n;
8397 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008398
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008399 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008400 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008401
8402 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008403 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008404 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008405 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008406 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008407 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008408 p--;
8409 }
8410 }
8411 sp = arglist.list;
8412 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008413 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008414 }
8415
8416 cmd_is_exec = 0;
8417 spclbltin = -1;
8418
8419 /* Now locate the command. */
8420 if (argc) {
8421 const char *oldpath;
8422 int cmd_flag = DO_ERR;
8423
8424 path += 5;
8425 oldpath = path;
8426 for (;;) {
8427 find_command(argv[0], &cmdentry, cmd_flag, path);
8428 if (cmdentry.cmdtype == CMDUNKNOWN) {
8429 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008430 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008431 goto bail;
8432 }
8433
8434 /* implement bltin and command here */
8435 if (cmdentry.cmdtype != CMDBUILTIN)
8436 break;
8437 if (spclbltin < 0)
8438 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8439 if (cmdentry.u.cmd == EXECCMD)
8440 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008441#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008442 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008443 path = oldpath;
8444 nargv = parse_command_args(argv, &path);
8445 if (!nargv)
8446 break;
8447 argc -= nargv - argv;
8448 argv = nargv;
8449 cmd_flag |= DO_NOFUNC;
8450 } else
8451#endif
8452 break;
8453 }
8454 }
8455
8456 if (status) {
8457 /* We have a redirection error. */
8458 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008459 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008460 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008461 exitstatus = status;
8462 goto out;
8463 }
8464
8465 /* Execute the command. */
8466 switch (cmdentry.cmdtype) {
8467 default:
8468 /* Fork off a child process if necessary. */
8469 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008470 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008471 jp = makejob(cmd, 1);
8472 if (forkshell(jp, cmd, FORK_FG) != 0) {
8473 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008474 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008475 break;
8476 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008477 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008478 }
8479 listsetvar(varlist.list, VEXPORT|VSTACK);
8480 shellexec(argv, path, cmdentry.u.index);
8481 /* NOTREACHED */
8482
8483 case CMDBUILTIN:
8484 cmdenviron = varlist.list;
8485 if (cmdenviron) {
8486 struct strlist *list = cmdenviron;
8487 int i = VNOSET;
8488 if (spclbltin > 0 || argc == 0) {
8489 i = 0;
8490 if (cmd_is_exec && argc > 1)
8491 i = VEXPORT;
8492 }
8493 listsetvar(list, i);
8494 }
8495 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8496 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008497 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008498 if (i == EXEXIT)
8499 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008500 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008501 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008502 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008503 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008504 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008505 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008506 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008507 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008508 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008509 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008510 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008511 }
8512 break;
8513
8514 case CMDFUNCTION:
8515 listsetvar(varlist.list, 0);
8516 if (evalfun(cmdentry.u.func, argc, argv, flags))
8517 goto raise;
8518 break;
8519 }
8520
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008521 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008522 popredir(cmd_is_exec);
8523 if (lastarg)
8524 /* dsl: I think this is intended to be used to support
8525 * '_' in 'vi' command mode during line editing...
8526 * However I implemented that within libedit itself.
8527 */
8528 setvar("_", lastarg, 0);
8529 popstackmark(&smark);
8530}
8531
8532static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008533evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8534{
Eric Andersenc470f442003-07-28 09:56:35 +00008535 char *volatile savecmdname;
8536 struct jmploc *volatile savehandler;
8537 struct jmploc jmploc;
8538 int i;
8539
8540 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008541 i = setjmp(jmploc.loc);
8542 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008543 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008544 savehandler = exception_handler;
8545 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008546 commandname = argv[0];
8547 argptr = argv + 1;
8548 optptr = NULL; /* initialize nextopt */
8549 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008550 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008551 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008552 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008553 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008554 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008555// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008556 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008557
8558 return i;
8559}
8560
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008561static int
8562goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008563{
8564 return !*endofname(p);
8565}
8566
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008567
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008568/*
8569 * Search for a command. This is called before we fork so that the
8570 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008571 * the child. The check for "goodname" is an overly conservative
8572 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008573 */
Eric Andersenc470f442003-07-28 09:56:35 +00008574static void
8575prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008576{
8577 struct cmdentry entry;
8578
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008579 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8580 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008581}
8582
Eric Andersencb57d552001-06-28 07:25:16 +00008583
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008584/* ============ Builtin commands
8585 *
8586 * Builtin commands whose functions are closely tied to evaluation
8587 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008588 */
8589
8590/*
Eric Andersencb57d552001-06-28 07:25:16 +00008591 * Handle break and continue commands. Break, continue, and return are
8592 * all handled by setting the evalskip flag. The evaluation routines
8593 * above all check this flag, and if it is set they start skipping
8594 * commands rather than executing them. The variable skipcount is
8595 * the number of loops to break/continue, or the number of function
8596 * levels to return. (The latter is always 1.) It should probably
8597 * be an error to break out of more loops than exist, but it isn't
8598 * in the standard shell so we don't make it one here.
8599 */
Eric Andersenc470f442003-07-28 09:56:35 +00008600static int
8601breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008602{
8603 int n = argc > 1 ? number(argv[1]) : 1;
8604
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008605 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008606 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008607 if (n > loopnest)
8608 n = loopnest;
8609 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008610 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008611 skipcount = n;
8612 }
8613 return 0;
8614}
8615
Eric Andersenc470f442003-07-28 09:56:35 +00008616
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008617/* ============ input.c
8618 *
Eric Andersen90898442003-08-06 11:20:52 +00008619 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008620 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008621
Eric Andersenc470f442003-07-28 09:56:35 +00008622#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008623
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008624enum {
8625 INPUT_PUSH_FILE = 1,
8626 INPUT_NOFILE_OK = 2,
8627};
Eric Andersencb57d552001-06-28 07:25:16 +00008628
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008629static int plinno = 1; /* input line number */
8630/* number of characters left in input buffer */
8631static int parsenleft; /* copy of parsefile->nleft */
8632static int parselleft; /* copy of parsefile->lleft */
8633/* next character in input buffer */
8634static char *parsenextc; /* copy of parsefile->nextc */
8635
8636static int checkkwd;
8637/* values of checkkwd variable */
8638#define CHKALIAS 0x1
8639#define CHKKWD 0x2
8640#define CHKNL 0x4
8641
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008642static void
8643popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008644{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008645 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008646
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008647 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008648#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008649 if (sp->ap) {
8650 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8651 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008652 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008653 if (sp->string != sp->ap->val) {
8654 free(sp->string);
8655 }
8656 sp->ap->flag &= ~ALIASINUSE;
8657 if (sp->ap->flag & ALIASDEAD) {
8658 unalias(sp->ap->name);
8659 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008660 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008661#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008662 parsenextc = sp->prevstring;
8663 parsenleft = sp->prevnleft;
8664/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8665 parsefile->strpush = sp->prev;
8666 if (sp != &(parsefile->basestrpush))
8667 free(sp);
8668 INT_ON;
8669}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008670
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008671static int
8672preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008673{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008674 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008675 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008676 parsenextc = buf;
8677
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008678 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008679#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008680 if (!iflag || parsefile->fd)
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008681 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008682 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008683#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008684 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008685#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008686 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8687 if (nr == 0) {
8688 /* Ctrl+C pressed */
8689 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008690 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008691 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008692 raise(SIGINT);
8693 return 1;
8694 }
Eric Andersenc470f442003-07-28 09:56:35 +00008695 goto retry;
8696 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008697 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008698 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008699 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008700 }
Eric Andersencb57d552001-06-28 07:25:16 +00008701 }
8702#else
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008703 nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008704#endif
8705
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008706#if 0
8707/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00008708 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008709 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008710 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008711 if (flags >= 0 && (flags & O_NONBLOCK)) {
8712 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008713 if (fcntl(0, F_SETFL, flags) >= 0) {
8714 out2str("sh: turning off NDELAY mode\n");
8715 goto retry;
8716 }
8717 }
8718 }
8719 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00008720#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008721 return nr;
8722}
8723
8724/*
8725 * Refill the input buffer and return the next input character:
8726 *
8727 * 1) If a string was pushed back on the input, pop it;
8728 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8729 * from a string so we can't refill the buffer, return EOF.
8730 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8731 * 4) Process input up to the next newline, deleting nul characters.
8732 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008733static int
Eric Andersenc470f442003-07-28 09:56:35 +00008734preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008735{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008736 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008737 int more;
8738 char savec;
8739
8740 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008741#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008742 if (parsenleft == -1 && parsefile->strpush->ap &&
8743 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008744 return PEOA;
8745 }
Eric Andersen2870d962001-07-02 17:27:21 +00008746#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008747 popstring();
8748 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008749 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008750 }
8751 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8752 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008753 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008754
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008755 more = parselleft;
8756 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008757 again:
8758 more = preadfd();
8759 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008760 parselleft = parsenleft = EOF_NLEFT;
8761 return PEOF;
8762 }
8763 }
8764
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008765 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008766
8767 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008768 for (;;) {
8769 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008770
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008771 more--;
8772 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008773
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008774 if (!c)
8775 memmove(q, q + 1, more);
8776 else {
8777 q++;
8778 if (c == '\n') {
8779 parsenleft = q - parsenextc - 1;
8780 break;
8781 }
Eric Andersencb57d552001-06-28 07:25:16 +00008782 }
8783
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008784 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008785 parsenleft = q - parsenextc - 1;
8786 if (parsenleft < 0)
8787 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008788 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008789 }
8790 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008791 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008792
8793 savec = *q;
8794 *q = '\0';
8795
8796 if (vflag) {
8797 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008798 }
8799
8800 *q = savec;
8801
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008802 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008803}
8804
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008805#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008806static int
8807pgetc(void)
8808{
8809 return pgetc_as_macro();
8810}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008811
8812#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8813#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008814#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008815#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008816#endif
8817
8818/*
8819 * Same as pgetc(), but ignores PEOA.
8820 */
8821#if ENABLE_ASH_ALIAS
8822static int
8823pgetc2(void)
8824{
8825 int c;
8826
8827 do {
8828 c = pgetc_macro();
8829 } while (c == PEOA);
8830 return c;
8831}
8832#else
8833static int
8834pgetc2(void)
8835{
8836 return pgetc_macro();
8837}
8838#endif
8839
8840/*
8841 * Read a line from the script.
8842 */
8843static char *
8844pfgets(char *line, int len)
8845{
8846 char *p = line;
8847 int nleft = len;
8848 int c;
8849
8850 while (--nleft > 0) {
8851 c = pgetc2();
8852 if (c == PEOF) {
8853 if (p == line)
8854 return NULL;
8855 break;
8856 }
8857 *p++ = c;
8858 if (c == '\n')
8859 break;
8860 }
8861 *p = '\0';
8862 return line;
8863}
8864
Eric Andersenc470f442003-07-28 09:56:35 +00008865/*
8866 * Undo the last call to pgetc. Only one character may be pushed back.
8867 * PEOF may be pushed back.
8868 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008869static void
Eric Andersenc470f442003-07-28 09:56:35 +00008870pungetc(void)
8871{
8872 parsenleft++;
8873 parsenextc--;
8874}
Eric Andersencb57d552001-06-28 07:25:16 +00008875
8876/*
8877 * Push a string back onto the input at this current parsefile level.
8878 * We handle aliases this way.
8879 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008880static void
Eric Andersenc470f442003-07-28 09:56:35 +00008881pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008882{
Eric Andersencb57d552001-06-28 07:25:16 +00008883 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008884 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008885
Eric Andersenc470f442003-07-28 09:56:35 +00008886 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008887 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008888/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8889 if (parsefile->strpush) {
Denis Vlasenko6aa74fc2008-02-21 04:35:14 +00008890 sp = ckzalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008891 sp->prev = parsefile->strpush;
8892 parsefile->strpush = sp;
8893 } else
8894 sp = parsefile->strpush = &(parsefile->basestrpush);
8895 sp->prevstring = parsenextc;
8896 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008897#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008898 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008899 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008900 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008901 sp->string = s;
8902 }
Eric Andersen2870d962001-07-02 17:27:21 +00008903#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008904 parsenextc = s;
8905 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008906 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008907}
8908
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008909/*
8910 * To handle the "." command, a stack of input files is used. Pushfile
8911 * adds a new entry to the stack and popfile restores the previous level.
8912 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008913static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008914pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008915{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008916 struct parsefile *pf;
8917
8918 parsefile->nleft = parsenleft;
8919 parsefile->lleft = parselleft;
8920 parsefile->nextc = parsenextc;
8921 parsefile->linno = plinno;
Denis Vlasenko597906c2008-02-20 16:38:54 +00008922 pf = ckzalloc(sizeof(*pf));
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008923 pf->prev = parsefile;
8924 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00008925 /*pf->strpush = NULL; - ckzalloc did it */
8926 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008927 parsefile = pf;
8928}
8929
8930static void
8931popfile(void)
8932{
8933 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008934
Denis Vlasenkob012b102007-02-19 22:43:01 +00008935 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008936 if (pf->fd >= 0)
8937 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008938 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008939 while (pf->strpush)
8940 popstring();
8941 parsefile = pf->prev;
8942 free(pf);
8943 parsenleft = parsefile->nleft;
8944 parselleft = parsefile->lleft;
8945 parsenextc = parsefile->nextc;
8946 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008947 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008948}
8949
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008950/*
8951 * Return to top level.
8952 */
8953static void
8954popallfiles(void)
8955{
8956 while (parsefile != &basepf)
8957 popfile();
8958}
8959
8960/*
8961 * Close the file(s) that the shell is reading commands from. Called
8962 * after a fork is done.
8963 */
8964static void
8965closescript(void)
8966{
8967 popallfiles();
8968 if (parsefile->fd > 0) {
8969 close(parsefile->fd);
8970 parsefile->fd = 0;
8971 }
8972}
8973
8974/*
8975 * Like setinputfile, but takes an open file descriptor. Call this with
8976 * interrupts off.
8977 */
8978static void
8979setinputfd(int fd, int push)
8980{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008981 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008982 if (push) {
8983 pushfile();
8984 parsefile->buf = 0;
8985 }
8986 parsefile->fd = fd;
8987 if (parsefile->buf == NULL)
8988 parsefile->buf = ckmalloc(IBUFSIZ);
8989 parselleft = parsenleft = 0;
8990 plinno = 1;
8991}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008992
Eric Andersenc470f442003-07-28 09:56:35 +00008993/*
8994 * Set the input to take input from a file. If push is set, push the
8995 * old input onto the stack first.
8996 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008997static int
8998setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008999{
9000 int fd;
9001 int fd2;
9002
Denis Vlasenkob012b102007-02-19 22:43:01 +00009003 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009004 fd = open(fname, O_RDONLY);
9005 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009006 if (flags & INPUT_NOFILE_OK)
9007 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009008 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009009 }
Eric Andersenc470f442003-07-28 09:56:35 +00009010 if (fd < 10) {
9011 fd2 = copyfd(fd, 10);
9012 close(fd);
9013 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009014 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009015 fd = fd2;
9016 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009017 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009018 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009019 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009020 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009021}
9022
Eric Andersencb57d552001-06-28 07:25:16 +00009023/*
9024 * Like setinputfile, but takes input from a string.
9025 */
Eric Andersenc470f442003-07-28 09:56:35 +00009026static void
9027setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009028{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009029 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009030 pushfile();
9031 parsenextc = string;
9032 parsenleft = strlen(string);
9033 parsefile->buf = NULL;
9034 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009035 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009036}
9037
9038
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009039/* ============ mail.c
9040 *
9041 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009042 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009043
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009044#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009045
Eric Andersencb57d552001-06-28 07:25:16 +00009046#define MAXMBOXES 10
9047
Eric Andersenc470f442003-07-28 09:56:35 +00009048/* times of mailboxes */
9049static time_t mailtime[MAXMBOXES];
9050/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009051static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009052
Eric Andersencb57d552001-06-28 07:25:16 +00009053/*
Eric Andersenc470f442003-07-28 09:56:35 +00009054 * Print appropriate message(s) if mail has arrived.
9055 * If mail_var_path_changed is set,
9056 * then the value of MAIL has mail_var_path_changed,
9057 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009058 */
Eric Andersenc470f442003-07-28 09:56:35 +00009059static void
9060chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009061{
Eric Andersencb57d552001-06-28 07:25:16 +00009062 const char *mpath;
9063 char *p;
9064 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009065 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009066 struct stackmark smark;
9067 struct stat statb;
9068
Eric Andersencb57d552001-06-28 07:25:16 +00009069 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009070 mpath = mpathset() ? mpathval() : mailval();
9071 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009072 p = padvance(&mpath, nullstr);
9073 if (p == NULL)
9074 break;
9075 if (*p == '\0')
9076 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009077 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009078#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009079 if (q[-1] != '/')
9080 abort();
9081#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009082 q[-1] = '\0'; /* delete trailing '/' */
9083 if (stat(p, &statb) < 0) {
9084 *mtp = 0;
9085 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009086 }
Eric Andersenc470f442003-07-28 09:56:35 +00009087 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9088 fprintf(
9089 stderr, snlfmt,
9090 pathopt ? pathopt : "you have mail"
9091 );
9092 }
9093 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009094 }
Eric Andersenc470f442003-07-28 09:56:35 +00009095 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009096 popstackmark(&smark);
9097}
Eric Andersencb57d552001-06-28 07:25:16 +00009098
Eric Andersenc470f442003-07-28 09:56:35 +00009099static void
9100changemail(const char *val)
9101{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009102 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009103}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009104
Denis Vlasenko131ae172007-02-18 13:00:19 +00009105#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009106
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009107
9108/* ============ ??? */
9109
Eric Andersencb57d552001-06-28 07:25:16 +00009110/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009111 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009112 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009113static void
9114setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009115{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009116 char **newparam;
9117 char **ap;
9118 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009119
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009120 for (nparam = 0; argv[nparam]; nparam++);
9121 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9122 while (*argv) {
9123 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009124 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009125 *ap = NULL;
9126 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009127 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009128 shellparam.nparam = nparam;
9129 shellparam.p = newparam;
9130#if ENABLE_ASH_GETOPTS
9131 shellparam.optind = 1;
9132 shellparam.optoff = -1;
9133#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009134}
9135
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009136/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009137 * Process shell options. The global variable argptr contains a pointer
9138 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009139 *
9140 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9141 * For a non-interactive shell, an error condition encountered
9142 * by a special built-in ... shall cause the shell to write a diagnostic message
9143 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009144 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009145 * ...
9146 * Utility syntax error (option or operand error) Shall exit
9147 * ...
9148 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9149 * we see that bash does not do that (set "finishes" with error code 1 instead,
9150 * and shell continues), and people rely on this behavior!
9151 * Testcase:
9152 * set -o barfoo 2>/dev/null
9153 * echo $?
9154 *
9155 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009156 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009157static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009158minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009159{
9160 int i;
9161
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009162 if (name) {
9163 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009164 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009165 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009166 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009167 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009168 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009169 ash_msg("illegal option -o %s", name);
9170 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009171 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009172 out1str("Current option settings\n");
9173 for (i = 0; i < NOPTS; i++)
9174 out1fmt("%-16s%s\n", optnames(i),
9175 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009176 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009177}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009178static void
9179setoption(int flag, int val)
9180{
9181 int i;
9182
9183 for (i = 0; i < NOPTS; i++) {
9184 if (optletters(i) == flag) {
9185 optlist[i] = val;
9186 return;
9187 }
9188 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009189 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009190 /* NOTREACHED */
9191}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009192static int
Eric Andersenc470f442003-07-28 09:56:35 +00009193options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009194{
9195 char *p;
9196 int val;
9197 int c;
9198
9199 if (cmdline)
9200 minusc = NULL;
9201 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009202 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009203 if (c != '-' && c != '+')
9204 break;
9205 argptr++;
9206 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009207 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009208 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009209 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009210 if (!cmdline) {
9211 /* "-" means turn off -x and -v */
9212 if (p[0] == '\0')
9213 xflag = vflag = 0;
9214 /* "--" means reset params */
9215 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009216 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009217 }
Eric Andersenc470f442003-07-28 09:56:35 +00009218 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009219 }
Eric Andersencb57d552001-06-28 07:25:16 +00009220 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009221 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009222 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009223 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009224 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009225 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009226 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009227 if (minus_o(*argptr, val)) {
9228 /* it already printed err message */
9229 return 1; /* error */
9230 }
Eric Andersencb57d552001-06-28 07:25:16 +00009231 if (*argptr)
9232 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009233 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9234 isloginsh = 1;
9235 /* bash does not accept +-login, we also won't */
9236 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009237 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009238 isloginsh = 1;
9239 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009240 } else {
9241 setoption(c, val);
9242 }
9243 }
9244 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009245 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009246}
9247
Eric Andersencb57d552001-06-28 07:25:16 +00009248/*
Eric Andersencb57d552001-06-28 07:25:16 +00009249 * The shift builtin command.
9250 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009251static int
Eric Andersenc470f442003-07-28 09:56:35 +00009252shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009253{
9254 int n;
9255 char **ap1, **ap2;
9256
9257 n = 1;
9258 if (argc > 1)
9259 n = number(argv[1]);
9260 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009261 ash_msg_and_raise_error("can't shift that many");
9262 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009263 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009264 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009265 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009266 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009267 }
9268 ap2 = shellparam.p;
9269 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009270#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009271 shellparam.optind = 1;
9272 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009273#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009274 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009275 return 0;
9276}
9277
Eric Andersencb57d552001-06-28 07:25:16 +00009278/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009279 * POSIX requires that 'set' (but not export or readonly) output the
9280 * variables in lexicographic order - by the locale's collating order (sigh).
9281 * Maybe we could keep them in an ordered balanced binary tree
9282 * instead of hashed lists.
9283 * For now just roll 'em through qsort for printing...
9284 */
9285static int
9286showvars(const char *sep_prefix, int on, int off)
9287{
9288 const char *sep;
9289 char **ep, **epend;
9290
9291 ep = listvars(on, off, &epend);
9292 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9293
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009294 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009295
9296 for (; ep < epend; ep++) {
9297 const char *p;
9298 const char *q;
9299
9300 p = strchrnul(*ep, '=');
9301 q = nullstr;
9302 if (*p)
9303 q = single_quote(++p);
9304 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9305 }
9306 return 0;
9307}
9308
9309/*
Eric Andersencb57d552001-06-28 07:25:16 +00009310 * The set command builtin.
9311 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009312static int
Eric Andersenc470f442003-07-28 09:56:35 +00009313setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009314{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009315 int retval;
9316
Eric Andersencb57d552001-06-28 07:25:16 +00009317 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009318 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009319 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009320 retval = 1;
9321 if (!options(0)) { /* if no parse error... */
9322 retval = 0;
9323 optschanged();
9324 if (*argptr != NULL) {
9325 setparam(argptr);
9326 }
Eric Andersencb57d552001-06-28 07:25:16 +00009327 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009328 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009329 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009330}
9331
Denis Vlasenko131ae172007-02-18 13:00:19 +00009332#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009333/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009334static void
9335change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009336{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009337 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009338 /* "get", generate */
9339 char buf[16];
9340
9341 rseed = rseed * 1103515245 + 12345;
9342 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9343 /* set without recursion */
9344 setvar(vrandom.text, buf, VNOFUNC);
9345 vrandom.flags &= ~VNOFUNC;
9346 } else {
9347 /* set/reset */
9348 rseed = strtoul(value, (char **)NULL, 10);
9349 }
Eric Andersenef02f822004-03-11 13:34:24 +00009350}
Eric Andersen16767e22004-03-16 05:14:10 +00009351#endif
9352
Denis Vlasenko131ae172007-02-18 13:00:19 +00009353#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009354static int
Eric Andersenc470f442003-07-28 09:56:35 +00009355getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009356{
9357 char *p, *q;
9358 char c = '?';
9359 int done = 0;
9360 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009361 char s[12];
9362 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009363
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009364 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009365 return 1;
9366 optnext = optfirst + *param_optind - 1;
9367
9368 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009369 p = NULL;
9370 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009371 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009372 if (p == NULL || *p == '\0') {
9373 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009374 p = *optnext;
9375 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009376 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009377 p = NULL;
9378 done = 1;
9379 goto out;
9380 }
9381 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009382 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009383 goto atend;
9384 }
9385
9386 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009387 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009388 if (*q == '\0') {
9389 if (optstr[0] == ':') {
9390 s[0] = c;
9391 s[1] = '\0';
9392 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009393 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009394 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009395 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009396 }
9397 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009398 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009399 }
9400 if (*++q == ':')
9401 q++;
9402 }
9403
9404 if (*++q == ':') {
9405 if (*p == '\0' && (p = *optnext) == NULL) {
9406 if (optstr[0] == ':') {
9407 s[0] = c;
9408 s[1] = '\0';
9409 err |= setvarsafe("OPTARG", s, 0);
9410 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009411 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009412 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009413 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009414 c = '?';
9415 }
Eric Andersenc470f442003-07-28 09:56:35 +00009416 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009417 }
9418
9419 if (p == *optnext)
9420 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009421 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009422 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009423 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009424 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009425 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009426 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009427 *param_optind = optnext - optfirst + 1;
9428 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009429 err |= setvarsafe("OPTIND", s, VNOFUNC);
9430 s[0] = c;
9431 s[1] = '\0';
9432 err |= setvarsafe(optvar, s, 0);
9433 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009434 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009435 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009436 flush_stdout_stderr();
9437 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009438 }
9439 return done;
9440}
Eric Andersenc470f442003-07-28 09:56:35 +00009441
9442/*
9443 * The getopts builtin. Shellparam.optnext points to the next argument
9444 * to be processed. Shellparam.optptr points to the next character to
9445 * be processed in the current argument. If shellparam.optnext is NULL,
9446 * then it's the first time getopts has been called.
9447 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009448static int
Eric Andersenc470f442003-07-28 09:56:35 +00009449getoptscmd(int argc, char **argv)
9450{
9451 char **optbase;
9452
9453 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009454 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009455 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009456 optbase = shellparam.p;
9457 if (shellparam.optind > shellparam.nparam + 1) {
9458 shellparam.optind = 1;
9459 shellparam.optoff = -1;
9460 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009461 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009462 optbase = &argv[3];
9463 if (shellparam.optind > argc - 2) {
9464 shellparam.optind = 1;
9465 shellparam.optoff = -1;
9466 }
9467 }
9468
9469 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009470 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009471}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009472#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009473
Eric Andersencb57d552001-06-28 07:25:16 +00009474
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009475/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009476
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009477/*
9478 * NEOF is returned by parsecmd when it encounters an end of file. It
9479 * must be distinct from NULL, so we use the address of a variable that
9480 * happens to be handy.
9481 */
9482static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009483#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009484static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009485static int lasttoken; /* last token read */
9486static char *wordtext; /* text of last word returned by readtoken */
9487static struct nodelist *backquotelist;
9488static union node *redirnode;
9489static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009490static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009491
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009492static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9493static void
9494raise_error_syntax(const char *msg)
9495{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009496 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009497 /* NOTREACHED */
9498}
9499
9500/*
9501 * Called when an unexpected token is read during the parse. The argument
9502 * is the token that is expected, or -1 if more than one type of token can
9503 * occur at this point.
9504 */
9505static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9506static void
9507raise_error_unexpected_syntax(int token)
9508{
9509 char msg[64];
9510 int l;
9511
9512 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9513 if (token >= 0)
9514 sprintf(msg + l, " (expecting %s)", tokname(token));
9515 raise_error_syntax(msg);
9516 /* NOTREACHED */
9517}
Eric Andersencb57d552001-06-28 07:25:16 +00009518
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009519#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009520
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009521struct heredoc {
9522 struct heredoc *next; /* next here document in list */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009523 union node *here; /* redirection node */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009524 char *eofmark; /* string indicating end of input */
9525 int striptabs; /* if set, strip leading tabs */
9526};
Eric Andersencb57d552001-06-28 07:25:16 +00009527
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009528static struct heredoc *heredoclist; /* list of here documents to read */
9529
9530/* parsing is heavily cross-recursive, need these forward decls */
9531static union node *andor(void);
9532static union node *pipeline(void);
9533static union node *parse_command(void);
9534static void parseheredoc(void);
9535static char peektoken(void);
9536static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009537
Eric Andersenc470f442003-07-28 09:56:35 +00009538static union node *
9539list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009540{
9541 union node *n1, *n2, *n3;
9542 int tok;
9543
Eric Andersenc470f442003-07-28 09:56:35 +00009544 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9545 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009546 return NULL;
9547 n1 = NULL;
9548 for (;;) {
9549 n2 = andor();
9550 tok = readtoken();
9551 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009552 if (n2->type == NPIPE) {
9553 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009554 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009555 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009556 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +00009557 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009558 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009559 n2 = n3;
9560 }
9561 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009562 }
9563 }
9564 if (n1 == NULL) {
9565 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009566 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009567 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009568 n3->type = NSEMI;
9569 n3->nbinary.ch1 = n1;
9570 n3->nbinary.ch2 = n2;
9571 n1 = n3;
9572 }
9573 switch (tok) {
9574 case TBACKGND:
9575 case TSEMI:
9576 tok = readtoken();
9577 /* fall through */
9578 case TNL:
9579 if (tok == TNL) {
9580 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009581 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009582 return n1;
9583 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009584 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009585 }
Eric Andersenc470f442003-07-28 09:56:35 +00009586 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009587 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009588 return n1;
9589 break;
9590 case TEOF:
9591 if (heredoclist)
9592 parseheredoc();
9593 else
Eric Andersenc470f442003-07-28 09:56:35 +00009594 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009595 return n1;
9596 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009597 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009598 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009599 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009600 return n1;
9601 }
9602 }
9603}
9604
Eric Andersenc470f442003-07-28 09:56:35 +00009605static union node *
9606andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009607{
Eric Andersencb57d552001-06-28 07:25:16 +00009608 union node *n1, *n2, *n3;
9609 int t;
9610
Eric Andersencb57d552001-06-28 07:25:16 +00009611 n1 = pipeline();
9612 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009613 t = readtoken();
9614 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009615 t = NAND;
9616 } else if (t == TOR) {
9617 t = NOR;
9618 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009619 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009620 return n1;
9621 }
Eric Andersenc470f442003-07-28 09:56:35 +00009622 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009623 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009624 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009625 n3->type = t;
9626 n3->nbinary.ch1 = n1;
9627 n3->nbinary.ch2 = n2;
9628 n1 = n3;
9629 }
9630}
9631
Eric Andersenc470f442003-07-28 09:56:35 +00009632static union node *
9633pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009634{
Eric Andersencb57d552001-06-28 07:25:16 +00009635 union node *n1, *n2, *pipenode;
9636 struct nodelist *lp, *prev;
9637 int negate;
9638
9639 negate = 0;
9640 TRACE(("pipeline: entered\n"));
9641 if (readtoken() == TNOT) {
9642 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009643 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009644 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009645 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009646 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009647 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009648 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009649 pipenode->type = NPIPE;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009650 /*pipenode->npipe.backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009651 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009652 pipenode->npipe.cmdlist = lp;
9653 lp->n = n1;
9654 do {
9655 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009656 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009657 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009658 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009659 prev->next = lp;
9660 } while (readtoken() == TPIPE);
9661 lp->next = NULL;
9662 n1 = pipenode;
9663 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009664 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009665 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009666 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009667 n2->type = NNOT;
9668 n2->nnot.com = n1;
9669 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009670 }
9671 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009672}
9673
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009674static union node *
9675makename(void)
9676{
9677 union node *n;
9678
Denis Vlasenko597906c2008-02-20 16:38:54 +00009679 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009680 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009681 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009682 n->narg.text = wordtext;
9683 n->narg.backquote = backquotelist;
9684 return n;
9685}
9686
9687static void
9688fixredir(union node *n, const char *text, int err)
9689{
9690 TRACE(("Fix redir %s %d\n", text, err));
9691 if (!err)
9692 n->ndup.vname = NULL;
9693
9694 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009695 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009696 else if (LONE_DASH(text))
9697 n->ndup.dupfd = -1;
9698 else {
9699 if (err)
9700 raise_error_syntax("Bad fd number");
9701 n->ndup.vname = makename();
9702 }
9703}
9704
9705/*
9706 * Returns true if the text contains nothing to expand (no dollar signs
9707 * or backquotes).
9708 */
9709static int
9710noexpand(char *text)
9711{
9712 char *p;
9713 char c;
9714
9715 p = text;
9716 while ((c = *p++) != '\0') {
9717 if (c == CTLQUOTEMARK)
9718 continue;
9719 if (c == CTLESC)
9720 p++;
9721 else if (SIT(c, BASESYNTAX) == CCTL)
9722 return 0;
9723 }
9724 return 1;
9725}
9726
9727static void
9728parsefname(void)
9729{
9730 union node *n = redirnode;
9731
9732 if (readtoken() != TWORD)
9733 raise_error_unexpected_syntax(-1);
9734 if (n->type == NHERE) {
9735 struct heredoc *here = heredoc;
9736 struct heredoc *p;
9737 int i;
9738
9739 if (quoteflag == 0)
9740 n->type = NXHERE;
9741 TRACE(("Here document %d\n", n->type));
9742 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9743 raise_error_syntax("Illegal eof marker for << redirection");
9744 rmescapes(wordtext);
9745 here->eofmark = wordtext;
9746 here->next = NULL;
9747 if (heredoclist == NULL)
9748 heredoclist = here;
9749 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009750 for (p = heredoclist; p->next; p = p->next)
9751 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009752 p->next = here;
9753 }
9754 } else if (n->type == NTOFD || n->type == NFROMFD) {
9755 fixredir(n, wordtext, 0);
9756 } else {
9757 n->nfile.fname = makename();
9758 }
9759}
Eric Andersencb57d552001-06-28 07:25:16 +00009760
Eric Andersenc470f442003-07-28 09:56:35 +00009761static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009762simplecmd(void)
9763{
9764 union node *args, **app;
9765 union node *n = NULL;
9766 union node *vars, **vpp;
9767 union node **rpp, *redir;
9768 int savecheckkwd;
9769
9770 args = NULL;
9771 app = &args;
9772 vars = NULL;
9773 vpp = &vars;
9774 redir = NULL;
9775 rpp = &redir;
9776
9777 savecheckkwd = CHKALIAS;
9778 for (;;) {
9779 checkkwd = savecheckkwd;
9780 switch (readtoken()) {
9781 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +00009782 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009783 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009784 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009785 n->narg.text = wordtext;
9786 n->narg.backquote = backquotelist;
9787 if (savecheckkwd && isassignment(wordtext)) {
9788 *vpp = n;
9789 vpp = &n->narg.next;
9790 } else {
9791 *app = n;
9792 app = &n->narg.next;
9793 savecheckkwd = 0;
9794 }
9795 break;
9796 case TREDIR:
9797 *rpp = n = redirnode;
9798 rpp = &n->nfile.next;
9799 parsefname(); /* read name of redirection file */
9800 break;
9801 case TLP:
9802 if (args && app == &args->narg.next
9803 && !vars && !redir
9804 ) {
9805 struct builtincmd *bcmd;
9806 const char *name;
9807
9808 /* We have a function */
9809 if (readtoken() != TRP)
9810 raise_error_unexpected_syntax(TRP);
9811 name = n->narg.text;
9812 if (!goodname(name)
9813 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9814 ) {
9815 raise_error_syntax("Bad function name");
9816 }
9817 n->type = NDEFUN;
9818 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9819 n->narg.next = parse_command();
9820 return n;
9821 }
9822 /* fall through */
9823 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009824 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009825 goto out;
9826 }
9827 }
9828 out:
9829 *app = NULL;
9830 *vpp = NULL;
9831 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009832 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009833 n->type = NCMD;
9834 n->ncmd.args = args;
9835 n->ncmd.assign = vars;
9836 n->ncmd.redirect = redir;
9837 return n;
9838}
9839
9840static union node *
9841parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009842{
Eric Andersencb57d552001-06-28 07:25:16 +00009843 union node *n1, *n2;
9844 union node *ap, **app;
9845 union node *cp, **cpp;
9846 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009847 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009848 int t;
9849
9850 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009851 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009852
Eric Andersencb57d552001-06-28 07:25:16 +00009853 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009854 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009855 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009856 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009857 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009858 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009859 n1->type = NIF;
9860 n1->nif.test = list(0);
9861 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009862 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009863 n1->nif.ifpart = list(0);
9864 n2 = n1;
9865 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009866 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009867 n2 = n2->nif.elsepart;
9868 n2->type = NIF;
9869 n2->nif.test = list(0);
9870 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009871 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009872 n2->nif.ifpart = list(0);
9873 }
9874 if (lasttoken == TELSE)
9875 n2->nif.elsepart = list(0);
9876 else {
9877 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009878 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009879 }
Eric Andersenc470f442003-07-28 09:56:35 +00009880 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009881 break;
9882 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009883 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009884 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009885 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009886 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009887 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009888 got = readtoken();
9889 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009890 TRACE(("expecting DO got %s %s\n", tokname(got),
9891 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009892 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009893 }
9894 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009895 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009896 break;
9897 }
9898 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009899 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009900 raise_error_syntax("Bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009901 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009902 n1->type = NFOR;
9903 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009904 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009905 if (readtoken() == TIN) {
9906 app = &ap;
9907 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009908 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009909 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009910 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009911 n2->narg.text = wordtext;
9912 n2->narg.backquote = backquotelist;
9913 *app = n2;
9914 app = &n2->narg.next;
9915 }
9916 *app = NULL;
9917 n1->nfor.args = ap;
9918 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009919 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009920 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009921 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009922 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009923 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009924 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009925 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00009926 n1->nfor.args = n2;
9927 /*
9928 * Newline or semicolon here is optional (but note
9929 * that the original Bourne shell only allowed NL).
9930 */
9931 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009932 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009933 }
Eric Andersenc470f442003-07-28 09:56:35 +00009934 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009935 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009936 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009937 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009938 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009939 break;
9940 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009941 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009942 n1->type = NCASE;
9943 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009944 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +00009945 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009946 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009947 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009948 n2->narg.text = wordtext;
9949 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +00009950 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009951 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009952 } while (readtoken() == TNL);
9953 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009954 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009955 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009956 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009957 checkkwd = CHKNL | CHKKWD;
9958 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009959 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009960 if (lasttoken == TLP)
9961 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +00009962 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009963 cp->type = NCLIST;
9964 app = &cp->nclist.pattern;
9965 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +00009966 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009967 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009968 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +00009969 ap->narg.text = wordtext;
9970 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009971 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009972 break;
9973 app = &ap->narg.next;
9974 readtoken();
9975 }
Denis Vlasenko597906c2008-02-20 16:38:54 +00009976 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009977 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009978 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009979 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009980
Eric Andersenc470f442003-07-28 09:56:35 +00009981 cpp = &cp->nclist.next;
9982
9983 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009984 t = readtoken();
9985 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009986 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009987 raise_error_unexpected_syntax(TENDCASE);
9988 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009989 }
Eric Andersenc470f442003-07-28 09:56:35 +00009990 }
Eric Andersencb57d552001-06-28 07:25:16 +00009991 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009992 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009993 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +00009994 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009995 n1->type = NSUBSHELL;
9996 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00009997 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +00009998 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009999 break;
10000 case TBEGIN:
10001 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010002 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010003 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010004 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010005 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010006 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010007 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010008 }
10009
Eric Andersenc470f442003-07-28 09:56:35 +000010010 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010011 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010012
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010013 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010014 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010015 checkkwd = CHKKWD | CHKALIAS;
10016 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010017 while (readtoken() == TREDIR) {
10018 *rpp = n2 = redirnode;
10019 rpp = &n2->nfile.next;
10020 parsefname();
10021 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010022 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010023 *rpp = NULL;
10024 if (redir) {
10025 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010026 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010027 n2->type = NREDIR;
10028 n2->nredir.n = n1;
10029 n1 = n2;
10030 }
10031 n1->nredir.redirect = redir;
10032 }
Eric Andersencb57d552001-06-28 07:25:16 +000010033 return n1;
10034}
10035
Eric Andersencb57d552001-06-28 07:25:16 +000010036/*
10037 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10038 * is not NULL, read a here document. In the latter case, eofmark is the
10039 * word which marks the end of the document and striptabs is true if
10040 * leading tabs should be stripped from the document. The argument firstc
10041 * is the first character of the input token or document.
10042 *
10043 * Because C does not have internal subroutines, I have simulated them
10044 * using goto's to implement the subroutine linkage. The following macros
10045 * will run code that appears at the end of readtoken1.
10046 */
10047
Eric Andersen2870d962001-07-02 17:27:21 +000010048#define CHECKEND() {goto checkend; checkend_return:;}
10049#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10050#define PARSESUB() {goto parsesub; parsesub_return:;}
10051#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10052#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10053#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010054
10055static int
Eric Andersenc470f442003-07-28 09:56:35 +000010056readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010057{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010058 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010059 int c = firstc;
10060 char *out;
10061 int len;
10062 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010063 struct nodelist *bqlist;
10064 smallint quotef;
10065 smallint dblquote;
10066 smallint oldstyle;
10067 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010068#if ENABLE_ASH_EXPAND_PRMT
10069 smallint pssyntax; /* we are expanding a prompt string */
10070#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010071 int varnest; /* levels of variables expansion */
10072 int arinest; /* levels of arithmetic expansion */
10073 int parenlevel; /* levels of parens in arithmetic */
10074 int dqvarnest; /* levels of variables expansion within double quotes */
10075
Eric Andersencb57d552001-06-28 07:25:16 +000010076#if __GNUC__
10077 /* Avoid longjmp clobbering */
10078 (void) &out;
10079 (void) &quotef;
10080 (void) &dblquote;
10081 (void) &varnest;
10082 (void) &arinest;
10083 (void) &parenlevel;
10084 (void) &dqvarnest;
10085 (void) &oldstyle;
10086 (void) &prevsyntax;
10087 (void) &syntax;
10088#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010089 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010090 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010091 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010092 oldstyle = 0;
10093 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010094#if ENABLE_ASH_EXPAND_PRMT
10095 pssyntax = (syntax == PSSYNTAX);
10096 if (pssyntax)
10097 syntax = DQSYNTAX;
10098#endif
10099 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010100 varnest = 0;
10101 arinest = 0;
10102 parenlevel = 0;
10103 dqvarnest = 0;
10104
10105 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010106 loop: { /* for each line, until end of word */
10107 CHECKEND(); /* set c to PEOF if at end of here document */
10108 for (;;) { /* until end of line or end of word */
10109 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010110 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010111 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010112 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010113 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010114 USTPUTC(c, out);
10115 plinno++;
10116 if (doprompt)
10117 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010118 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010119 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010120 case CWORD:
10121 USTPUTC(c, out);
10122 break;
10123 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010124 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010125 USTPUTC(CTLESC, out);
10126 USTPUTC(c, out);
10127 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010128 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010129 c = pgetc2();
10130 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010131 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010132 USTPUTC('\\', out);
10133 pungetc();
10134 } else if (c == '\n') {
10135 if (doprompt)
10136 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010137 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010138#if ENABLE_ASH_EXPAND_PRMT
10139 if (c == '$' && pssyntax) {
10140 USTPUTC(CTLESC, out);
10141 USTPUTC('\\', out);
10142 }
10143#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010144 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010145 c != '\\' && c != '`' &&
10146 c != '$' && (
10147 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010148 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010149 ) {
10150 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010151 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010152 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010153 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010154 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010155 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010156 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010157 }
10158 break;
10159 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010160 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010161 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010162 if (eofmark == NULL) {
10163 USTPUTC(CTLQUOTEMARK, out);
10164 }
Eric Andersencb57d552001-06-28 07:25:16 +000010165 break;
10166 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010167 syntax = DQSYNTAX;
10168 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010169 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010170 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010171 if (eofmark != NULL && arinest == 0
10172 && varnest == 0
10173 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010174 USTPUTC(c, out);
10175 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010176 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010177 syntax = BASESYNTAX;
10178 dblquote = 0;
10179 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010180 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010181 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010182 }
10183 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010184 case CVAR: /* '$' */
10185 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010186 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010187 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010188 if (varnest > 0) {
10189 varnest--;
10190 if (dqvarnest > 0) {
10191 dqvarnest--;
10192 }
10193 USTPUTC(CTLENDVAR, out);
10194 } else {
10195 USTPUTC(c, out);
10196 }
10197 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010198#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010199 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010200 parenlevel++;
10201 USTPUTC(c, out);
10202 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010203 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010204 if (parenlevel > 0) {
10205 USTPUTC(c, out);
10206 --parenlevel;
10207 } else {
10208 if (pgetc() == ')') {
10209 if (--arinest == 0) {
10210 USTPUTC(CTLENDARI, out);
10211 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010212 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010213 } else
10214 USTPUTC(')', out);
10215 } else {
10216 /*
10217 * unbalanced parens
10218 * (don't 2nd guess - no error)
10219 */
10220 pungetc();
10221 USTPUTC(')', out);
10222 }
10223 }
10224 break;
10225#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010226 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010227 PARSEBACKQOLD();
10228 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010229 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010230 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010231 case CIGN:
10232 break;
10233 default:
10234 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010235 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010236#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010237 if (c != PEOA)
10238#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010239 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010240
Eric Andersencb57d552001-06-28 07:25:16 +000010241 }
10242 c = pgetc_macro();
10243 }
10244 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010245 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010246#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010247 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010248 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010249#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010250 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010251 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010252 if (varnest != 0) {
10253 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010254 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010255 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010256 }
10257 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010258 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010259 out = stackblock();
10260 if (eofmark == NULL) {
10261 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010262 && quotef == 0
10263 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010264 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010265 PARSEREDIR();
10266 return lasttoken = TREDIR;
10267 } else {
10268 pungetc();
10269 }
10270 }
10271 quoteflag = quotef;
10272 backquotelist = bqlist;
10273 grabstackblock(len);
10274 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010275 lasttoken = TWORD;
10276 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010277/* end of readtoken routine */
10278
Eric Andersencb57d552001-06-28 07:25:16 +000010279/*
10280 * Check to see whether we are at the end of the here document. When this
10281 * is called, c is set to the first character of the next input line. If
10282 * we are at the end of the here document, this routine sets the c to PEOF.
10283 */
Eric Andersenc470f442003-07-28 09:56:35 +000010284checkend: {
10285 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010286#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010287 if (c == PEOA) {
10288 c = pgetc2();
10289 }
10290#endif
10291 if (striptabs) {
10292 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010293 c = pgetc2();
10294 }
Eric Andersenc470f442003-07-28 09:56:35 +000010295 }
10296 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010297 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010298 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010299
Eric Andersenc470f442003-07-28 09:56:35 +000010300 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010301 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010302 if (*p == '\n' && *q == '\0') {
10303 c = PEOF;
10304 plinno++;
10305 needprompt = doprompt;
10306 } else {
10307 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010308 }
10309 }
10310 }
10311 }
Eric Andersenc470f442003-07-28 09:56:35 +000010312 goto checkend_return;
10313}
Eric Andersencb57d552001-06-28 07:25:16 +000010314
Eric Andersencb57d552001-06-28 07:25:16 +000010315/*
10316 * Parse a redirection operator. The variable "out" points to a string
10317 * specifying the fd to be redirected. The variable "c" contains the
10318 * first character of the redirection operator.
10319 */
Eric Andersenc470f442003-07-28 09:56:35 +000010320parseredir: {
10321 char fd = *out;
10322 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010323
Denis Vlasenko597906c2008-02-20 16:38:54 +000010324 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010325 if (c == '>') {
10326 np->nfile.fd = 1;
10327 c = pgetc();
10328 if (c == '>')
10329 np->type = NAPPEND;
10330 else if (c == '|')
10331 np->type = NCLOBBER;
10332 else if (c == '&')
10333 np->type = NTOFD;
10334 else {
10335 np->type = NTO;
10336 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010337 }
Eric Andersenc470f442003-07-28 09:56:35 +000010338 } else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000010339 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010340 c = pgetc();
10341 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010342 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010343 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010344 np = stzalloc(sizeof(struct nhere));
10345 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010346 }
10347 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010348 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010349 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010350 c = pgetc();
10351 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010352 heredoc->striptabs = 1;
10353 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010354 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010355 pungetc();
10356 }
10357 break;
10358
10359 case '&':
10360 np->type = NFROMFD;
10361 break;
10362
10363 case '>':
10364 np->type = NFROMTO;
10365 break;
10366
10367 default:
10368 np->type = NFROM;
10369 pungetc();
10370 break;
10371 }
Eric Andersencb57d552001-06-28 07:25:16 +000010372 }
Eric Andersenc470f442003-07-28 09:56:35 +000010373 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010374 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010375 redirnode = np;
10376 goto parseredir_return;
10377}
Eric Andersencb57d552001-06-28 07:25:16 +000010378
Eric Andersencb57d552001-06-28 07:25:16 +000010379/*
10380 * Parse a substitution. At this point, we have read the dollar sign
10381 * and nothing else.
10382 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010383
10384/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10385 * (assuming ascii char codes, as the original implementation did) */
10386#define is_special(c) \
10387 ((((unsigned int)c) - 33 < 32) \
10388 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010389parsesub: {
10390 int subtype;
10391 int typeloc;
10392 int flags;
10393 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010394 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010395
Eric Andersenc470f442003-07-28 09:56:35 +000010396 c = pgetc();
10397 if (
10398 c <= PEOA_OR_PEOF ||
10399 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10400 ) {
10401 USTPUTC('$', out);
10402 pungetc();
10403 } else if (c == '(') { /* $(command) or $((arith)) */
10404 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010405#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010406 PARSEARITH();
10407#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010408 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010409#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010410 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010411 pungetc();
10412 PARSEBACKQNEW();
10413 }
10414 } else {
10415 USTPUTC(CTLVAR, out);
10416 typeloc = out - (char *)stackblock();
10417 USTPUTC(VSNORMAL, out);
10418 subtype = VSNORMAL;
10419 if (c == '{') {
10420 c = pgetc();
10421 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010422 c = pgetc();
10423 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010424 c = '#';
10425 else
10426 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010427 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010428 subtype = 0;
10429 }
10430 if (c > PEOA_OR_PEOF && is_name(c)) {
10431 do {
10432 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010433 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010434 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010435 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010436 do {
10437 STPUTC(c, out);
10438 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010439 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010440 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010441 USTPUTC(c, out);
10442 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010443 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010444 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010445
Eric Andersenc470f442003-07-28 09:56:35 +000010446 STPUTC('=', out);
10447 flags = 0;
10448 if (subtype == 0) {
10449 switch (c) {
10450 case ':':
10451 flags = VSNUL;
10452 c = pgetc();
10453 /*FALLTHROUGH*/
10454 default:
10455 p = strchr(types, c);
10456 if (p == NULL)
10457 goto badsub;
10458 subtype = p - types + VSNORMAL;
10459 break;
10460 case '%':
10461 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010462 {
10463 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010464 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010465 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010466 c = pgetc();
10467 if (c == cc)
10468 subtype++;
10469 else
10470 pungetc();
10471 break;
10472 }
10473 }
Eric Andersenc470f442003-07-28 09:56:35 +000010474 } else {
10475 pungetc();
10476 }
10477 if (dblquote || arinest)
10478 flags |= VSQUOTE;
10479 *((char *)stackblock() + typeloc) = subtype | flags;
10480 if (subtype != VSNORMAL) {
10481 varnest++;
10482 if (dblquote || arinest) {
10483 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010484 }
10485 }
10486 }
Eric Andersenc470f442003-07-28 09:56:35 +000010487 goto parsesub_return;
10488}
Eric Andersencb57d552001-06-28 07:25:16 +000010489
Eric Andersencb57d552001-06-28 07:25:16 +000010490/*
10491 * Called to parse command substitutions. Newstyle is set if the command
10492 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10493 * list of commands (passed by reference), and savelen is the number of
10494 * characters on the top of the stack which must be preserved.
10495 */
Eric Andersenc470f442003-07-28 09:56:35 +000010496parsebackq: {
10497 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010498 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010499 union node *n;
10500 char *volatile str;
10501 struct jmploc jmploc;
10502 struct jmploc *volatile savehandler;
10503 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010504 smallint saveprompt = 0;
10505
Eric Andersencb57d552001-06-28 07:25:16 +000010506#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010507 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010508#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010509 savepbq = parsebackquote;
10510 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010511 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010512 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010513 exception_handler = savehandler;
10514 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010515 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010516 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010517 str = NULL;
10518 savelen = out - (char *)stackblock();
10519 if (savelen > 0) {
10520 str = ckmalloc(savelen);
10521 memcpy(str, stackblock(), savelen);
10522 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010523 savehandler = exception_handler;
10524 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010525 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010526 if (oldstyle) {
10527 /* We must read until the closing backquote, giving special
10528 treatment to some slashes, and then push the string and
10529 reread it as input, interpreting it normally. */
10530 char *pout;
10531 int pc;
10532 size_t psavelen;
10533 char *pstr;
10534
10535
10536 STARTSTACKSTR(pout);
10537 for (;;) {
10538 if (needprompt) {
10539 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010540 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010541 pc = pgetc();
10542 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010543 case '`':
10544 goto done;
10545
10546 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010547 pc = pgetc();
10548 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010549 plinno++;
10550 if (doprompt)
10551 setprompt(2);
10552 /*
10553 * If eating a newline, avoid putting
10554 * the newline into the new character
10555 * stream (via the STPUTC after the
10556 * switch).
10557 */
10558 continue;
10559 }
10560 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010561 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010562 STPUTC('\\', pout);
10563 if (pc > PEOA_OR_PEOF) {
10564 break;
10565 }
10566 /* fall through */
10567
10568 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010569#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010570 case PEOA:
10571#endif
10572 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010573 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010574
10575 case '\n':
10576 plinno++;
10577 needprompt = doprompt;
10578 break;
10579
10580 default:
10581 break;
10582 }
10583 STPUTC(pc, pout);
10584 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010585 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010586 STPUTC('\0', pout);
10587 psavelen = pout - (char *)stackblock();
10588 if (psavelen > 0) {
10589 pstr = grabstackstr(pout);
10590 setinputstring(pstr);
10591 }
10592 }
10593 nlpp = &bqlist;
10594 while (*nlpp)
10595 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010596 *nlpp = stzalloc(sizeof(**nlpp));
10597 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010598 parsebackquote = oldstyle;
10599
10600 if (oldstyle) {
10601 saveprompt = doprompt;
10602 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010603 }
10604
Eric Andersenc470f442003-07-28 09:56:35 +000010605 n = list(2);
10606
10607 if (oldstyle)
10608 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010609 else if (readtoken() != TRP)
10610 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010611
10612 (*nlpp)->n = n;
10613 if (oldstyle) {
10614 /*
10615 * Start reading from old file again, ignoring any pushed back
10616 * tokens left from the backquote parsing
10617 */
10618 popfile();
10619 tokpushback = 0;
10620 }
10621 while (stackblocksize() <= savelen)
10622 growstackblock();
10623 STARTSTACKSTR(out);
10624 if (str) {
10625 memcpy(out, str, savelen);
10626 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010627 INT_OFF;
10628 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010629 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010630 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010631 }
10632 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010633 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010634 if (arinest || dblquote)
10635 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10636 else
10637 USTPUTC(CTLBACKQ, out);
10638 if (oldstyle)
10639 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010640 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010641}
10642
Denis Vlasenko131ae172007-02-18 13:00:19 +000010643#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010644/*
10645 * Parse an arithmetic expansion (indicate start of one and set state)
10646 */
Eric Andersenc470f442003-07-28 09:56:35 +000010647parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010648 if (++arinest == 1) {
10649 prevsyntax = syntax;
10650 syntax = ARISYNTAX;
10651 USTPUTC(CTLARI, out);
10652 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010653 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010654 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010655 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010656 } else {
10657 /*
10658 * we collapse embedded arithmetic expansion to
10659 * parenthesis, which should be equivalent
10660 */
10661 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010662 }
Eric Andersenc470f442003-07-28 09:56:35 +000010663 goto parsearith_return;
10664}
10665#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010666
Eric Andersenc470f442003-07-28 09:56:35 +000010667} /* end of readtoken */
10668
Eric Andersencb57d552001-06-28 07:25:16 +000010669/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010670 * Read the next input token.
10671 * If the token is a word, we set backquotelist to the list of cmds in
10672 * backquotes. We set quoteflag to true if any part of the word was
10673 * quoted.
10674 * If the token is TREDIR, then we set redirnode to a structure containing
10675 * the redirection.
10676 * In all cases, the variable startlinno is set to the number of the line
10677 * on which the token starts.
10678 *
10679 * [Change comment: here documents and internal procedures]
10680 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10681 * word parsing code into a separate routine. In this case, readtoken
10682 * doesn't need to have any internal procedures, but parseword does.
10683 * We could also make parseoperator in essence the main routine, and
10684 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010685 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010686#define NEW_xxreadtoken
10687#ifdef NEW_xxreadtoken
10688/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010689static const char xxreadtoken_chars[7] ALIGN1 = {
10690 '\n', '(', ')', '&', '|', ';', 0
10691};
Eric Andersencb57d552001-06-28 07:25:16 +000010692
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010693static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010694 TNL, TLP, TRP, /* only single occurrence allowed */
10695 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10696 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010697 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010698};
10699
10700#define xxreadtoken_doubles \
10701 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10702#define xxreadtoken_singles \
10703 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10704
10705static int
10706xxreadtoken(void)
10707{
10708 int c;
10709
10710 if (tokpushback) {
10711 tokpushback = 0;
10712 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010713 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010714 if (needprompt) {
10715 setprompt(2);
10716 }
10717 startlinno = plinno;
10718 for (;;) { /* until token or start of word found */
10719 c = pgetc_macro();
10720
10721 if ((c != ' ') && (c != '\t')
10722#if ENABLE_ASH_ALIAS
10723 && (c != PEOA)
10724#endif
10725 ) {
10726 if (c == '#') {
10727 while ((c = pgetc()) != '\n' && c != PEOF);
10728 pungetc();
10729 } else if (c == '\\') {
10730 if (pgetc() != '\n') {
10731 pungetc();
10732 goto READTOKEN1;
10733 }
10734 startlinno = ++plinno;
10735 if (doprompt)
10736 setprompt(2);
10737 } else {
10738 const char *p
10739 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10740
10741 if (c != PEOF) {
10742 if (c == '\n') {
10743 plinno++;
10744 needprompt = doprompt;
10745 }
10746
10747 p = strchr(xxreadtoken_chars, c);
10748 if (p == NULL) {
10749 READTOKEN1:
10750 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10751 }
10752
10753 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10754 if (pgetc() == *p) { /* double occurrence? */
10755 p += xxreadtoken_doubles + 1;
10756 } else {
10757 pungetc();
10758 }
10759 }
10760 }
10761 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10762 }
10763 }
10764 } /* for */
10765}
10766#else
10767#define RETURN(token) return lasttoken = token
10768static int
10769xxreadtoken(void)
10770{
10771 int c;
10772
10773 if (tokpushback) {
10774 tokpushback = 0;
10775 return lasttoken;
10776 }
10777 if (needprompt) {
10778 setprompt(2);
10779 }
10780 startlinno = plinno;
10781 for (;;) { /* until token or start of word found */
10782 c = pgetc_macro();
10783 switch (c) {
10784 case ' ': case '\t':
10785#if ENABLE_ASH_ALIAS
10786 case PEOA:
10787#endif
10788 continue;
10789 case '#':
10790 while ((c = pgetc()) != '\n' && c != PEOF);
10791 pungetc();
10792 continue;
10793 case '\\':
10794 if (pgetc() == '\n') {
10795 startlinno = ++plinno;
10796 if (doprompt)
10797 setprompt(2);
10798 continue;
10799 }
10800 pungetc();
10801 goto breakloop;
10802 case '\n':
10803 plinno++;
10804 needprompt = doprompt;
10805 RETURN(TNL);
10806 case PEOF:
10807 RETURN(TEOF);
10808 case '&':
10809 if (pgetc() == '&')
10810 RETURN(TAND);
10811 pungetc();
10812 RETURN(TBACKGND);
10813 case '|':
10814 if (pgetc() == '|')
10815 RETURN(TOR);
10816 pungetc();
10817 RETURN(TPIPE);
10818 case ';':
10819 if (pgetc() == ';')
10820 RETURN(TENDCASE);
10821 pungetc();
10822 RETURN(TSEMI);
10823 case '(':
10824 RETURN(TLP);
10825 case ')':
10826 RETURN(TRP);
10827 default:
10828 goto breakloop;
10829 }
10830 }
10831 breakloop:
10832 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10833#undef RETURN
10834}
10835#endif /* NEW_xxreadtoken */
10836
10837static int
10838readtoken(void)
10839{
10840 int t;
10841#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010842 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010843#endif
10844
10845#if ENABLE_ASH_ALIAS
10846 top:
10847#endif
10848
10849 t = xxreadtoken();
10850
10851 /*
10852 * eat newlines
10853 */
10854 if (checkkwd & CHKNL) {
10855 while (t == TNL) {
10856 parseheredoc();
10857 t = xxreadtoken();
10858 }
10859 }
10860
10861 if (t != TWORD || quoteflag) {
10862 goto out;
10863 }
10864
10865 /*
10866 * check for keywords
10867 */
10868 if (checkkwd & CHKKWD) {
10869 const char *const *pp;
10870
10871 pp = findkwd(wordtext);
10872 if (pp) {
10873 lasttoken = t = pp - tokname_array;
10874 TRACE(("keyword %s recognized\n", tokname(t)));
10875 goto out;
10876 }
10877 }
10878
10879 if (checkkwd & CHKALIAS) {
10880#if ENABLE_ASH_ALIAS
10881 struct alias *ap;
10882 ap = lookupalias(wordtext, 1);
10883 if (ap != NULL) {
10884 if (*ap->val) {
10885 pushstring(ap->val, ap);
10886 }
10887 goto top;
10888 }
10889#endif
10890 }
10891 out:
10892 checkkwd = 0;
10893#if DEBUG
10894 if (!alreadyseen)
10895 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10896 else
10897 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10898#endif
10899 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010900}
10901
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010902static char
10903peektoken(void)
10904{
10905 int t;
10906
10907 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010908 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010909 return tokname_array[t][0];
10910}
Eric Andersencb57d552001-06-28 07:25:16 +000010911
10912/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010913 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10914 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010915 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010916static union node *
10917parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010918{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010919 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010920
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010921 tokpushback = 0;
10922 doprompt = interact;
10923 if (doprompt)
10924 setprompt(doprompt);
10925 needprompt = 0;
10926 t = readtoken();
10927 if (t == TEOF)
10928 return NEOF;
10929 if (t == TNL)
10930 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010931 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010932 return list(1);
10933}
10934
10935/*
10936 * Input any here documents.
10937 */
10938static void
10939parseheredoc(void)
10940{
10941 struct heredoc *here;
10942 union node *n;
10943
10944 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010945 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010946
10947 while (here) {
10948 if (needprompt) {
10949 setprompt(2);
10950 }
10951 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10952 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010953 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010954 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010955 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010956 n->narg.text = wordtext;
10957 n->narg.backquote = backquotelist;
10958 here->here->nhere.doc = n;
10959 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010960 }
Eric Andersencb57d552001-06-28 07:25:16 +000010961}
10962
10963
10964/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010965 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010966 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010967#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010968static const char *
10969expandstr(const char *ps)
10970{
10971 union node n;
10972
10973 /* XXX Fix (char *) cast. */
10974 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010975 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010976 popfile();
10977
10978 n.narg.type = NARG;
10979 n.narg.next = NULL;
10980 n.narg.text = wordtext;
10981 n.narg.backquote = backquotelist;
10982
10983 expandarg(&n, NULL, 0);
10984 return stackblock();
10985}
10986#endif
10987
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010988/*
10989 * Execute a command or commands contained in a string.
10990 */
10991static int
10992evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010993{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010994 union node *n;
10995 struct stackmark smark;
10996 int skip;
10997
10998 setinputstring(s);
10999 setstackmark(&smark);
11000
11001 skip = 0;
11002 while ((n = parsecmd(0)) != NEOF) {
11003 evaltree(n, 0);
11004 popstackmark(&smark);
11005 skip = evalskip;
11006 if (skip)
11007 break;
11008 }
11009 popfile();
11010
11011 skip &= mask;
11012 evalskip = skip;
11013 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011014}
11015
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011016/*
11017 * The eval command.
11018 */
11019static int
11020evalcmd(int argc, char **argv)
11021{
11022 char *p;
11023 char *concat;
11024 char **ap;
11025
11026 if (argc > 1) {
11027 p = argv[1];
11028 if (argc > 2) {
11029 STARTSTACKSTR(concat);
11030 ap = argv + 2;
11031 for (;;) {
11032 concat = stack_putstr(p, concat);
11033 p = *ap++;
11034 if (p == NULL)
11035 break;
11036 STPUTC(' ', concat);
11037 }
11038 STPUTC('\0', concat);
11039 p = grabstackstr(concat);
11040 }
11041 evalstring(p, ~SKIPEVAL);
11042
11043 }
11044 return exitstatus;
11045}
11046
11047/*
11048 * Read and execute commands. "Top" is nonzero for the top level command
11049 * loop; it turns on prompting if the shell is interactive.
11050 */
11051static int
11052cmdloop(int top)
11053{
11054 union node *n;
11055 struct stackmark smark;
11056 int inter;
11057 int numeof = 0;
11058
11059 TRACE(("cmdloop(%d) called\n", top));
11060 for (;;) {
11061 int skip;
11062
11063 setstackmark(&smark);
11064#if JOBS
11065 if (jobctl)
11066 showjobs(stderr, SHOW_CHANGED);
11067#endif
11068 inter = 0;
11069 if (iflag && top) {
11070 inter++;
11071#if ENABLE_ASH_MAIL
11072 chkmail();
11073#endif
11074 }
11075 n = parsecmd(inter);
11076 /* showtree(n); DEBUG */
11077 if (n == NEOF) {
11078 if (!top || numeof >= 50)
11079 break;
11080 if (!stoppedjobs()) {
11081 if (!Iflag)
11082 break;
11083 out2str("\nUse \"exit\" to leave shell.\n");
11084 }
11085 numeof++;
11086 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011087 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11088 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011089 numeof = 0;
11090 evaltree(n, 0);
11091 }
11092 popstackmark(&smark);
11093 skip = evalskip;
11094
11095 if (skip) {
11096 evalskip = 0;
11097 return skip & SKIPEVAL;
11098 }
11099 }
11100 return 0;
11101}
11102
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011103/*
11104 * Take commands from a file. To be compatible we should do a path
11105 * search for the file, which is necessary to find sub-commands.
11106 */
11107static char *
11108find_dot_file(char *name)
11109{
11110 char *fullname;
11111 const char *path = pathval();
11112 struct stat statb;
11113
11114 /* don't try this for absolute or relative paths */
11115 if (strchr(name, '/'))
11116 return name;
11117
11118 while ((fullname = padvance(&path, name)) != NULL) {
11119 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11120 /*
11121 * Don't bother freeing here, since it will
11122 * be freed by the caller.
11123 */
11124 return fullname;
11125 }
11126 stunalloc(fullname);
11127 }
11128
11129 /* not found in the PATH */
11130 ash_msg_and_raise_error("%s: not found", name);
11131 /* NOTREACHED */
11132}
11133
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011134static int
11135dotcmd(int argc, char **argv)
11136{
11137 struct strlist *sp;
11138 volatile struct shparam saveparam;
11139 int status = 0;
11140
11141 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011142 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011143
11144 if (argc >= 2) { /* That's what SVR2 does */
11145 char *fullname;
11146
11147 fullname = find_dot_file(argv[1]);
11148
11149 if (argc > 2) {
11150 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011151 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011152 shellparam.nparam = argc - 2;
11153 shellparam.p = argv + 2;
11154 };
11155
11156 setinputfile(fullname, INPUT_PUSH_FILE);
11157 commandname = fullname;
11158 cmdloop(0);
11159 popfile();
11160
11161 if (argc > 2) {
11162 freeparam(&shellparam);
11163 shellparam = saveparam;
11164 };
11165 status = exitstatus;
11166 }
11167 return status;
11168}
11169
11170static int
11171exitcmd(int argc, char **argv)
11172{
11173 if (stoppedjobs())
11174 return 0;
11175 if (argc > 1)
11176 exitstatus = number(argv[1]);
11177 raise_exception(EXEXIT);
11178 /* NOTREACHED */
11179}
11180
11181#if ENABLE_ASH_BUILTIN_ECHO
11182static int
11183echocmd(int argc, char **argv)
11184{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011185 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011186}
11187#endif
11188
11189#if ENABLE_ASH_BUILTIN_TEST
11190static int
11191testcmd(int argc, char **argv)
11192{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011193 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011194}
11195#endif
11196
11197/*
11198 * Read a file containing shell functions.
11199 */
11200static void
11201readcmdfile(char *name)
11202{
11203 setinputfile(name, INPUT_PUSH_FILE);
11204 cmdloop(0);
11205 popfile();
11206}
11207
11208
Denis Vlasenkocc571512007-02-23 21:10:35 +000011209/* ============ find_command inplementation */
11210
11211/*
11212 * Resolve a command name. If you change this routine, you may have to
11213 * change the shellexec routine as well.
11214 */
11215static void
11216find_command(char *name, struct cmdentry *entry, int act, const char *path)
11217{
11218 struct tblentry *cmdp;
11219 int idx;
11220 int prev;
11221 char *fullname;
11222 struct stat statb;
11223 int e;
11224 int updatetbl;
11225 struct builtincmd *bcmd;
11226
11227 /* If name contains a slash, don't use PATH or hash table */
11228 if (strchr(name, '/') != NULL) {
11229 entry->u.index = -1;
11230 if (act & DO_ABS) {
11231 while (stat(name, &statb) < 0) {
11232#ifdef SYSV
11233 if (errno == EINTR)
11234 continue;
11235#endif
11236 entry->cmdtype = CMDUNKNOWN;
11237 return;
11238 }
11239 }
11240 entry->cmdtype = CMDNORMAL;
11241 return;
11242 }
11243
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011244/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011245
11246 updatetbl = (path == pathval());
11247 if (!updatetbl) {
11248 act |= DO_ALTPATH;
11249 if (strstr(path, "%builtin") != NULL)
11250 act |= DO_ALTBLTIN;
11251 }
11252
11253 /* If name is in the table, check answer will be ok */
11254 cmdp = cmdlookup(name, 0);
11255 if (cmdp != NULL) {
11256 int bit;
11257
11258 switch (cmdp->cmdtype) {
11259 default:
11260#if DEBUG
11261 abort();
11262#endif
11263 case CMDNORMAL:
11264 bit = DO_ALTPATH;
11265 break;
11266 case CMDFUNCTION:
11267 bit = DO_NOFUNC;
11268 break;
11269 case CMDBUILTIN:
11270 bit = DO_ALTBLTIN;
11271 break;
11272 }
11273 if (act & bit) {
11274 updatetbl = 0;
11275 cmdp = NULL;
11276 } else if (cmdp->rehash == 0)
11277 /* if not invalidated by cd, we're done */
11278 goto success;
11279 }
11280
11281 /* If %builtin not in path, check for builtin next */
11282 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011283 if (bcmd) {
11284 if (IS_BUILTIN_REGULAR(bcmd))
11285 goto builtin_success;
11286 if (act & DO_ALTPATH) {
11287 if (!(act & DO_ALTBLTIN))
11288 goto builtin_success;
11289 } else if (builtinloc <= 0) {
11290 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011291 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011292 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011293
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011294#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011295 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011296 entry->cmdtype = CMDNORMAL;
11297 entry->u.index = -1;
11298 return;
11299 }
11300#endif
11301
Denis Vlasenkocc571512007-02-23 21:10:35 +000011302 /* We have to search path. */
11303 prev = -1; /* where to start */
11304 if (cmdp && cmdp->rehash) { /* doing a rehash */
11305 if (cmdp->cmdtype == CMDBUILTIN)
11306 prev = builtinloc;
11307 else
11308 prev = cmdp->param.index;
11309 }
11310
11311 e = ENOENT;
11312 idx = -1;
11313 loop:
11314 while ((fullname = padvance(&path, name)) != NULL) {
11315 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011316 /* NB: code below will still use fullname
11317 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011318 idx++;
11319 if (pathopt) {
11320 if (prefix(pathopt, "builtin")) {
11321 if (bcmd)
11322 goto builtin_success;
11323 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011324 } else if (!(act & DO_NOFUNC)
11325 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011326 /* handled below */
11327 } else {
11328 /* ignore unimplemented options */
11329 continue;
11330 }
11331 }
11332 /* if rehash, don't redo absolute path names */
11333 if (fullname[0] == '/' && idx <= prev) {
11334 if (idx < prev)
11335 continue;
11336 TRACE(("searchexec \"%s\": no change\n", name));
11337 goto success;
11338 }
11339 while (stat(fullname, &statb) < 0) {
11340#ifdef SYSV
11341 if (errno == EINTR)
11342 continue;
11343#endif
11344 if (errno != ENOENT && errno != ENOTDIR)
11345 e = errno;
11346 goto loop;
11347 }
11348 e = EACCES; /* if we fail, this will be the error */
11349 if (!S_ISREG(statb.st_mode))
11350 continue;
11351 if (pathopt) { /* this is a %func directory */
11352 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011353 /* NB: stalloc will return space pointed by fullname
11354 * (because we don't have any intervening allocations
11355 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011356 readcmdfile(fullname);
11357 cmdp = cmdlookup(name, 0);
11358 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11359 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11360 stunalloc(fullname);
11361 goto success;
11362 }
11363 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11364 if (!updatetbl) {
11365 entry->cmdtype = CMDNORMAL;
11366 entry->u.index = idx;
11367 return;
11368 }
11369 INT_OFF;
11370 cmdp = cmdlookup(name, 1);
11371 cmdp->cmdtype = CMDNORMAL;
11372 cmdp->param.index = idx;
11373 INT_ON;
11374 goto success;
11375 }
11376
11377 /* We failed. If there was an entry for this command, delete it */
11378 if (cmdp && updatetbl)
11379 delete_cmd_entry();
11380 if (act & DO_ERR)
11381 ash_msg("%s: %s", name, errmsg(e, "not found"));
11382 entry->cmdtype = CMDUNKNOWN;
11383 return;
11384
11385 builtin_success:
11386 if (!updatetbl) {
11387 entry->cmdtype = CMDBUILTIN;
11388 entry->u.cmd = bcmd;
11389 return;
11390 }
11391 INT_OFF;
11392 cmdp = cmdlookup(name, 1);
11393 cmdp->cmdtype = CMDBUILTIN;
11394 cmdp->param.cmd = bcmd;
11395 INT_ON;
11396 success:
11397 cmdp->rehash = 0;
11398 entry->cmdtype = cmdp->cmdtype;
11399 entry->u = cmdp->param;
11400}
11401
11402
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011403/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011404
Eric Andersencb57d552001-06-28 07:25:16 +000011405/*
Eric Andersencb57d552001-06-28 07:25:16 +000011406 * The trap builtin.
11407 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011408static int
Eric Andersenc470f442003-07-28 09:56:35 +000011409trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011410{
11411 char *action;
11412 char **ap;
11413 int signo;
11414
Eric Andersenc470f442003-07-28 09:56:35 +000011415 nextopt(nullstr);
11416 ap = argptr;
11417 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011418 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011419 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011420 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011421
Rob Landleyc9c1a412006-07-12 19:17:55 +000011422 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011423 out1fmt("trap -- %s %s\n",
11424 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011425 }
11426 }
11427 return 0;
11428 }
Eric Andersenc470f442003-07-28 09:56:35 +000011429 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011430 action = NULL;
11431 else
11432 action = *ap++;
11433 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011434 signo = get_signum(*ap);
11435 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011436 ash_msg_and_raise_error("%s: bad trap", *ap);
11437 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011438 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011439 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011440 action = NULL;
11441 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011442 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011443 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011444 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011445 trap[signo] = action;
11446 if (signo != 0)
11447 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011448 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011449 ap++;
11450 }
11451 return 0;
11452}
11453
Eric Andersenc470f442003-07-28 09:56:35 +000011454
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011455/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011456
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011457#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011458/*
11459 * Lists available builtins
11460 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011461static int
11462helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011463{
11464 int col, i;
11465
11466 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011467 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011468 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011469 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011470 if (col > 60) {
11471 out1fmt("\n");
11472 col = 0;
11473 }
11474 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011475#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011476 {
11477 const char *a = applet_names;
11478 while (*a) {
11479 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11480 if (col > 60) {
11481 out1fmt("\n");
11482 col = 0;
11483 }
11484 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011485 }
11486 }
11487#endif
11488 out1fmt("\n\n");
11489 return EXIT_SUCCESS;
11490}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011491#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011492
Eric Andersencb57d552001-06-28 07:25:16 +000011493/*
Eric Andersencb57d552001-06-28 07:25:16 +000011494 * The export and readonly commands.
11495 */
Eric Andersenc470f442003-07-28 09:56:35 +000011496static int
11497exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011498{
11499 struct var *vp;
11500 char *name;
11501 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011502 char **aptr;
11503 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011504
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011505 if (nextopt("p") != 'p') {
11506 aptr = argptr;
11507 name = *aptr;
11508 if (name) {
11509 do {
11510 p = strchr(name, '=');
11511 if (p != NULL) {
11512 p++;
11513 } else {
11514 vp = *findvar(hashvar(name), name);
11515 if (vp) {
11516 vp->flags |= flag;
11517 continue;
11518 }
Eric Andersencb57d552001-06-28 07:25:16 +000011519 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011520 setvar(name, p, flag);
11521 } while ((name = *++aptr) != NULL);
11522 return 0;
11523 }
Eric Andersencb57d552001-06-28 07:25:16 +000011524 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011525 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011526 return 0;
11527}
11528
Eric Andersencb57d552001-06-28 07:25:16 +000011529/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011530 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011531 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011532static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011533unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011534{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011535 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011536
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011537 cmdp = cmdlookup(name, 0);
11538 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11539 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011540}
11541
Eric Andersencb57d552001-06-28 07:25:16 +000011542/*
Eric Andersencb57d552001-06-28 07:25:16 +000011543 * The unset builtin command. We unset the function before we unset the
11544 * variable to allow a function to be unset when there is a readonly variable
11545 * with the same name.
11546 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011547static int
Eric Andersenc470f442003-07-28 09:56:35 +000011548unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011549{
11550 char **ap;
11551 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011552 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011553 int ret = 0;
11554
11555 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011556 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011557 }
Eric Andersencb57d552001-06-28 07:25:16 +000011558
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011559 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011560 if (flag != 'f') {
11561 i = unsetvar(*ap);
11562 ret |= i;
11563 if (!(i & 2))
11564 continue;
11565 }
11566 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011567 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011568 }
Eric Andersenc470f442003-07-28 09:56:35 +000011569 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011570}
11571
11572
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011573/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011574
Eric Andersenc470f442003-07-28 09:56:35 +000011575#include <sys/times.h>
11576
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011577static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011578 ' ', offsetof(struct tms, tms_utime),
11579 '\n', offsetof(struct tms, tms_stime),
11580 ' ', offsetof(struct tms, tms_cutime),
11581 '\n', offsetof(struct tms, tms_cstime),
11582 0
11583};
Eric Andersencb57d552001-06-28 07:25:16 +000011584
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011585static int
11586timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011587{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011588 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011589 const unsigned char *p;
11590 struct tms buf;
11591
11592 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011593 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011594
11595 p = timescmd_str;
11596 do {
11597 t = *(clock_t *)(((char *) &buf) + p[1]);
11598 s = t / clk_tck;
11599 out1fmt("%ldm%ld.%.3lds%c",
11600 s/60, s%60,
11601 ((t - s * clk_tck) * 1000) / clk_tck,
11602 p[0]);
11603 } while (*(p += 2));
11604
Eric Andersencb57d552001-06-28 07:25:16 +000011605 return 0;
11606}
11607
Denis Vlasenko131ae172007-02-18 13:00:19 +000011608#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011609static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011610dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011611{
Eric Andersened9ecf72004-06-22 08:29:45 +000011612 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011613 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011614
Denis Vlasenkob012b102007-02-19 22:43:01 +000011615 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011616 result = arith(s, &errcode);
11617 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011618 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011619 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011620 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011621 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011622 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011623 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011624 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011625 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011626 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011627
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011628 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011629}
Eric Andersenc470f442003-07-28 09:56:35 +000011630
Eric Andersenc470f442003-07-28 09:56:35 +000011631/*
Eric Andersen90898442003-08-06 11:20:52 +000011632 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11633 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11634 *
11635 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011636 */
11637static int
Eric Andersen90898442003-08-06 11:20:52 +000011638letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011639{
Eric Andersenc470f442003-07-28 09:56:35 +000011640 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011641 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011642
Eric Andersen90898442003-08-06 11:20:52 +000011643 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011644 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011645 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011646 for (ap = argv + 1; *ap; ap++) {
11647 i = dash_arith(*ap);
11648 }
Eric Andersenc470f442003-07-28 09:56:35 +000011649
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011650 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011651}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011652#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011653
Eric Andersenc470f442003-07-28 09:56:35 +000011654
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011655/* ============ miscbltin.c
11656 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011657 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011658 */
11659
11660#undef rflag
11661
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011662#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011663typedef enum __rlimit_resource rlim_t;
11664#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011665
Eric Andersenc470f442003-07-28 09:56:35 +000011666/*
11667 * The read builtin. The -e option causes backslashes to escape the
11668 * following character.
11669 *
11670 * This uses unbuffered input, which may be avoidable in some cases.
11671 */
Eric Andersenc470f442003-07-28 09:56:35 +000011672static int
11673readcmd(int argc, char **argv)
11674{
11675 char **ap;
11676 int backslash;
11677 char c;
11678 int rflag;
11679 char *prompt;
11680 const char *ifs;
11681 char *p;
11682 int startword;
11683 int status;
11684 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011685#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011686 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011687 int nchars = 0;
11688 int silent = 0;
11689 struct termios tty, old_tty;
11690#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011691#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011692 fd_set set;
11693 struct timeval ts;
11694
11695 ts.tv_sec = ts.tv_usec = 0;
11696#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011697
11698 rflag = 0;
11699 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011700#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011701 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011702#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011703 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011704#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011705 while ((i = nextopt("p:rt:")) != '\0')
11706#else
11707 while ((i = nextopt("p:r")) != '\0')
11708#endif
11709 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011710 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011711 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011712 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011713 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011714#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011715 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011716 nchars = bb_strtou(optionarg, NULL, 10);
11717 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011718 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011719 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011720 break;
11721 case 's':
11722 silent = 1;
11723 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011724#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011725#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011726 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011727 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011728 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011729 /* EINVAL means number is ok, but not terminated by NUL */
11730 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011731 char *p2;
11732 if (*++p) {
11733 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011734 ts.tv_usec = bb_strtou(p, &p2, 10);
11735 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011736 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011737 scale = p2 - p;
11738 /* normalize to usec */
11739 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011740 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011741 while (scale++ < 6)
11742 ts.tv_usec *= 10;
11743 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011744 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011745 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011746 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011747 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011748 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011749 }
Paul Fox02eb9342005-09-07 16:56:02 +000011750 break;
11751#endif
11752 case 'r':
11753 rflag = 1;
11754 break;
11755 default:
11756 break;
11757 }
Eric Andersenc470f442003-07-28 09:56:35 +000011758 }
11759 if (prompt && isatty(0)) {
11760 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011761 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011762 ap = argptr;
11763 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011764 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011765 ifs = bltinlookup("IFS");
11766 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011767 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011768#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011769 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011770 if (tcgetattr(0, &tty) != 0) {
11771 /* Not a tty */
11772 n_flag = 0;
11773 silent = 0;
11774 } else {
11775 old_tty = tty;
11776 if (n_flag) {
11777 tty.c_lflag &= ~ICANON;
11778 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11779 }
11780 if (silent) {
11781 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11782 }
11783 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011784 }
Paul Fox02eb9342005-09-07 16:56:02 +000011785 }
11786#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011787#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011788 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011789 FD_ZERO(&set);
11790 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011791
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011792 /* poll-based wait produces bigger code, using select */
11793 i = select(1, &set, NULL, NULL, &ts);
11794 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011795#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011796 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011797 tcsetattr(0, TCSANOW, &old_tty);
11798#endif
11799 return 1;
11800 }
11801 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011802#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011803 status = 0;
11804 startword = 1;
11805 backslash = 0;
11806 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011807 do {
Denis Vlasenkoe376d452008-02-20 22:23:24 +000011808 if (nonblock_safe_read(0, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011809 status = 1;
11810 break;
11811 }
11812 if (c == '\0')
11813 continue;
11814 if (backslash) {
11815 backslash = 0;
11816 if (c != '\n')
11817 goto put;
11818 continue;
11819 }
11820 if (!rflag && c == '\\') {
11821 backslash++;
11822 continue;
11823 }
11824 if (c == '\n')
11825 break;
11826 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11827 continue;
11828 }
11829 startword = 0;
11830 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11831 STACKSTRNUL(p);
11832 setvar(*ap, stackblock(), 0);
11833 ap++;
11834 startword = 1;
11835 STARTSTACKSTR(p);
11836 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011837 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011838 STPUTC(c, p);
11839 }
11840 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011841/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011842#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011843 while (!n_flag || --nchars);
11844#else
11845 while (1);
11846#endif
11847
11848#if ENABLE_ASH_READ_NCHARS
11849 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011850 tcsetattr(0, TCSANOW, &old_tty);
11851#endif
11852
Eric Andersenc470f442003-07-28 09:56:35 +000011853 STACKSTRNUL(p);
11854 /* Remove trailing blanks */
11855 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11856 *p = '\0';
11857 setvar(*ap, stackblock(), 0);
11858 while (*++ap != NULL)
11859 setvar(*ap, nullstr, 0);
11860 return status;
11861}
11862
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011863static int
11864umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011865{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011866 static const char permuser[3] ALIGN1 = "ugo";
11867 static const char permmode[3] ALIGN1 = "rwx";
11868 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011869 S_IRUSR, S_IWUSR, S_IXUSR,
11870 S_IRGRP, S_IWGRP, S_IXGRP,
11871 S_IROTH, S_IWOTH, S_IXOTH
11872 };
11873
11874 char *ap;
11875 mode_t mask;
11876 int i;
11877 int symbolic_mode = 0;
11878
11879 while (nextopt("S") != '\0') {
11880 symbolic_mode = 1;
11881 }
11882
Denis Vlasenkob012b102007-02-19 22:43:01 +000011883 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011884 mask = umask(0);
11885 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011886 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011887
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011888 ap = *argptr;
11889 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011890 if (symbolic_mode) {
11891 char buf[18];
11892 char *p = buf;
11893
11894 for (i = 0; i < 3; i++) {
11895 int j;
11896
11897 *p++ = permuser[i];
11898 *p++ = '=';
11899 for (j = 0; j < 3; j++) {
11900 if ((mask & permmask[3 * i + j]) == 0) {
11901 *p++ = permmode[j];
11902 }
11903 }
11904 *p++ = ',';
11905 }
11906 *--p = 0;
11907 puts(buf);
11908 } else {
11909 out1fmt("%.4o\n", mask);
11910 }
11911 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011912 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011913 mask = 0;
11914 do {
11915 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011916 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011917 mask = (mask << 3) + (*ap - '0');
11918 } while (*++ap != '\0');
11919 umask(mask);
11920 } else {
11921 mask = ~mask & 0777;
11922 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011923 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011924 }
11925 umask(~mask & 0777);
11926 }
11927 }
11928 return 0;
11929}
11930
11931/*
11932 * ulimit builtin
11933 *
11934 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11935 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11936 * ash by J.T. Conklin.
11937 *
11938 * Public domain.
11939 */
11940
11941struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011942 uint8_t cmd; /* RLIMIT_xxx fit into it */
11943 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011944 char option;
11945};
11946
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011947static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011948#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011949 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011950#endif
11951#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011952 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011953#endif
11954#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011955 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011956#endif
11957#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011958 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011959#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011960#ifdef RLIMIT_CORE
11961 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011962#endif
11963#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011964 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011965#endif
11966#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011967 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011968#endif
11969#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011970 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011971#endif
11972#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011973 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011974#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011975#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011976 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011977#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011978#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011979 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011980#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011981};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011982static const char limits_name[] =
11983#ifdef RLIMIT_CPU
11984 "time(seconds)" "\0"
11985#endif
11986#ifdef RLIMIT_FSIZE
11987 "file(blocks)" "\0"
11988#endif
11989#ifdef RLIMIT_DATA
11990 "data(kb)" "\0"
11991#endif
11992#ifdef RLIMIT_STACK
11993 "stack(kb)" "\0"
11994#endif
11995#ifdef RLIMIT_CORE
11996 "coredump(blocks)" "\0"
11997#endif
11998#ifdef RLIMIT_RSS
11999 "memory(kb)" "\0"
12000#endif
12001#ifdef RLIMIT_MEMLOCK
12002 "locked memory(kb)" "\0"
12003#endif
12004#ifdef RLIMIT_NPROC
12005 "process" "\0"
12006#endif
12007#ifdef RLIMIT_NOFILE
12008 "nofiles" "\0"
12009#endif
12010#ifdef RLIMIT_AS
12011 "vmemory(kb)" "\0"
12012#endif
12013#ifdef RLIMIT_LOCKS
12014 "locks" "\0"
12015#endif
12016;
Eric Andersenc470f442003-07-28 09:56:35 +000012017
Glenn L McGrath76620622004-01-13 10:19:37 +000012018enum limtype { SOFT = 0x1, HARD = 0x2 };
12019
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012020static void
12021printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012022 const struct limits *l)
12023{
12024 rlim_t val;
12025
12026 val = limit->rlim_max;
12027 if (how & SOFT)
12028 val = limit->rlim_cur;
12029
12030 if (val == RLIM_INFINITY)
12031 out1fmt("unlimited\n");
12032 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012033 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012034 out1fmt("%lld\n", (long long) val);
12035 }
12036}
12037
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012038static int
Eric Andersenc470f442003-07-28 09:56:35 +000012039ulimitcmd(int argc, char **argv)
12040{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012041 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012042 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012043 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012044 const struct limits *l;
12045 int set, all = 0;
12046 int optc, what;
12047 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012048
12049 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012050 while ((optc = nextopt("HSa"
12051#ifdef RLIMIT_CPU
12052 "t"
12053#endif
12054#ifdef RLIMIT_FSIZE
12055 "f"
12056#endif
12057#ifdef RLIMIT_DATA
12058 "d"
12059#endif
12060#ifdef RLIMIT_STACK
12061 "s"
12062#endif
12063#ifdef RLIMIT_CORE
12064 "c"
12065#endif
12066#ifdef RLIMIT_RSS
12067 "m"
12068#endif
12069#ifdef RLIMIT_MEMLOCK
12070 "l"
12071#endif
12072#ifdef RLIMIT_NPROC
12073 "p"
12074#endif
12075#ifdef RLIMIT_NOFILE
12076 "n"
12077#endif
12078#ifdef RLIMIT_AS
12079 "v"
12080#endif
12081#ifdef RLIMIT_LOCKS
12082 "w"
12083#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012084 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012085 switch (optc) {
12086 case 'H':
12087 how = HARD;
12088 break;
12089 case 'S':
12090 how = SOFT;
12091 break;
12092 case 'a':
12093 all = 1;
12094 break;
12095 default:
12096 what = optc;
12097 }
12098
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012099 for (l = limits_tbl; l->option != what; l++)
12100 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012101
12102 set = *argptr ? 1 : 0;
12103 if (set) {
12104 char *p = *argptr;
12105
12106 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012107 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012108 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012109 val = RLIM_INFINITY;
12110 else {
12111 val = (rlim_t) 0;
12112
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012113 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012114 val = (val * 10) + (long)(c - '0');
12115 if (val < (rlim_t) 0)
12116 break;
12117 }
12118 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012119 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012120 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012121 }
12122 }
12123 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012124 const char *lname = limits_name;
12125 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012126 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012127 out1fmt("%-20s ", lname);
12128 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012129 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012130 }
12131 return 0;
12132 }
12133
12134 getrlimit(l->cmd, &limit);
12135 if (set) {
12136 if (how & HARD)
12137 limit.rlim_max = val;
12138 if (how & SOFT)
12139 limit.rlim_cur = val;
12140 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012141 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012142 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012143 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012144 }
12145 return 0;
12146}
12147
Eric Andersen90898442003-08-06 11:20:52 +000012148
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012149/* ============ Math support */
12150
Denis Vlasenko131ae172007-02-18 13:00:19 +000012151#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012152
12153/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12154
12155 Permission is hereby granted, free of charge, to any person obtaining
12156 a copy of this software and associated documentation files (the
12157 "Software"), to deal in the Software without restriction, including
12158 without limitation the rights to use, copy, modify, merge, publish,
12159 distribute, sublicense, and/or sell copies of the Software, and to
12160 permit persons to whom the Software is furnished to do so, subject to
12161 the following conditions:
12162
12163 The above copyright notice and this permission notice shall be
12164 included in all copies or substantial portions of the Software.
12165
12166 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12167 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12168 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12169 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12170 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12171 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12172 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12173*/
12174
12175/* This is my infix parser/evaluator. It is optimized for size, intended
12176 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012177 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012178 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012179 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012180 * be that which POSIX specifies for shells. */
12181
12182/* The code uses a simple two-stack algorithm. See
12183 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012184 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012185 * this is based (this code differs in that it applies operators immediately
12186 * to the stack instead of adding them to a queue to end up with an
12187 * expression). */
12188
12189/* To use the routine, call it with an expression string and error return
12190 * pointer */
12191
12192/*
12193 * Aug 24, 2001 Manuel Novoa III
12194 *
12195 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12196 *
12197 * 1) In arith_apply():
12198 * a) Cached values of *numptr and &(numptr[-1]).
12199 * b) Removed redundant test for zero denominator.
12200 *
12201 * 2) In arith():
12202 * a) Eliminated redundant code for processing operator tokens by moving
12203 * to a table-based implementation. Also folded handling of parens
12204 * into the table.
12205 * b) Combined all 3 loops which called arith_apply to reduce generated
12206 * code size at the cost of speed.
12207 *
12208 * 3) The following expressions were treated as valid by the original code:
12209 * 1() , 0! , 1 ( *3 ) .
12210 * These bugs have been fixed by internally enclosing the expression in
12211 * parens and then checking that all binary ops and right parens are
12212 * preceded by a valid expression (NUM_TOKEN).
12213 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012214 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012215 * ctype's isspace() if it is used by another busybox applet or if additional
12216 * whitespace chars should be considered. Look below the "#include"s for a
12217 * precompiler test.
12218 */
12219
12220/*
12221 * Aug 26, 2001 Manuel Novoa III
12222 *
12223 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12224 *
12225 * Merge in Aaron's comments previously posted to the busybox list,
12226 * modified slightly to take account of my changes to the code.
12227 *
12228 */
12229
12230/*
12231 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12232 *
12233 * - allow access to variable,
12234 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12235 * - realize assign syntax (VAR=expr, +=, *= etc)
12236 * - realize exponentiation (** operator)
12237 * - realize comma separated - expr, expr
12238 * - realise ++expr --expr expr++ expr--
12239 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012240 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012241 * - was restored loses XOR operator
12242 * - remove one goto label, added three ;-)
12243 * - protect $((num num)) as true zero expr (Manuel`s error)
12244 * - always use special isspace(), see comment from bash ;-)
12245 */
12246
Eric Andersen90898442003-08-06 11:20:52 +000012247#define arith_isspace(arithval) \
12248 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12249
Eric Andersen90898442003-08-06 11:20:52 +000012250typedef unsigned char operator;
12251
12252/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012253 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012254 * precedence. The ID portion is so that multiple operators can have the
12255 * same precedence, ensuring that the leftmost one is evaluated first.
12256 * Consider * and /. */
12257
12258#define tok_decl(prec,id) (((id)<<5)|(prec))
12259#define PREC(op) ((op) & 0x1F)
12260
12261#define TOK_LPAREN tok_decl(0,0)
12262
12263#define TOK_COMMA tok_decl(1,0)
12264
12265#define TOK_ASSIGN tok_decl(2,0)
12266#define TOK_AND_ASSIGN tok_decl(2,1)
12267#define TOK_OR_ASSIGN tok_decl(2,2)
12268#define TOK_XOR_ASSIGN tok_decl(2,3)
12269#define TOK_PLUS_ASSIGN tok_decl(2,4)
12270#define TOK_MINUS_ASSIGN tok_decl(2,5)
12271#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12272#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12273
12274#define TOK_MUL_ASSIGN tok_decl(3,0)
12275#define TOK_DIV_ASSIGN tok_decl(3,1)
12276#define TOK_REM_ASSIGN tok_decl(3,2)
12277
12278/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012279#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012280
12281/* conditional is right associativity too */
12282#define TOK_CONDITIONAL tok_decl(4,0)
12283#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12284
12285#define TOK_OR tok_decl(5,0)
12286
12287#define TOK_AND tok_decl(6,0)
12288
12289#define TOK_BOR tok_decl(7,0)
12290
12291#define TOK_BXOR tok_decl(8,0)
12292
12293#define TOK_BAND tok_decl(9,0)
12294
12295#define TOK_EQ tok_decl(10,0)
12296#define TOK_NE tok_decl(10,1)
12297
12298#define TOK_LT tok_decl(11,0)
12299#define TOK_GT tok_decl(11,1)
12300#define TOK_GE tok_decl(11,2)
12301#define TOK_LE tok_decl(11,3)
12302
12303#define TOK_LSHIFT tok_decl(12,0)
12304#define TOK_RSHIFT tok_decl(12,1)
12305
12306#define TOK_ADD tok_decl(13,0)
12307#define TOK_SUB tok_decl(13,1)
12308
12309#define TOK_MUL tok_decl(14,0)
12310#define TOK_DIV tok_decl(14,1)
12311#define TOK_REM tok_decl(14,2)
12312
12313/* exponent is right associativity */
12314#define TOK_EXPONENT tok_decl(15,1)
12315
12316/* For now unary operators. */
12317#define UNARYPREC 16
12318#define TOK_BNOT tok_decl(UNARYPREC,0)
12319#define TOK_NOT tok_decl(UNARYPREC,1)
12320
12321#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12322#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12323
12324#define PREC_PRE (UNARYPREC+2)
12325
12326#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12327#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12328
12329#define PREC_POST (UNARYPREC+3)
12330
12331#define TOK_POST_INC tok_decl(PREC_POST, 0)
12332#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12333
12334#define SPEC_PREC (UNARYPREC+4)
12335
12336#define TOK_NUM tok_decl(SPEC_PREC, 0)
12337#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12338
12339#define NUMPTR (*numstackptr)
12340
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012341static int
12342tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012343{
12344 operator prec = PREC(op);
12345
12346 convert_prec_is_assing(prec);
12347 return (prec == PREC(TOK_ASSIGN) ||
12348 prec == PREC_PRE || prec == PREC_POST);
12349}
12350
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012351static int
12352is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012353{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012354 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12355 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012356}
12357
Eric Andersen90898442003-08-06 11:20:52 +000012358typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012359 arith_t val;
12360 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012361 char contidional_second_val_initialized;
12362 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012363 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012364} v_n_t;
12365
Eric Andersen90898442003-08-06 11:20:52 +000012366typedef struct CHK_VAR_RECURSIVE_LOOPED {
12367 const char *var;
12368 struct CHK_VAR_RECURSIVE_LOOPED *next;
12369} chk_var_recursive_looped_t;
12370
12371static chk_var_recursive_looped_t *prev_chk_var_recursive;
12372
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012373static int
12374arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012375{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 if (t->var) {
12377 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012378
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012379 if (p) {
12380 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012381
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012382 /* recursive try as expression */
12383 chk_var_recursive_looped_t *cur;
12384 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012385
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12387 if (strcmp(cur->var, t->var) == 0) {
12388 /* expression recursion loop detected */
12389 return -5;
12390 }
12391 }
12392 /* save current lookuped var name */
12393 cur = prev_chk_var_recursive;
12394 cur_save.var = t->var;
12395 cur_save.next = cur;
12396 prev_chk_var_recursive = &cur_save;
12397
12398 t->val = arith (p, &errcode);
12399 /* restore previous ptr after recursiving */
12400 prev_chk_var_recursive = cur;
12401 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012402 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012403 /* allow undefined var as 0 */
12404 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012405 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012406 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012407}
12408
12409/* "applying" a token means performing it on the top elements on the integer
12410 * stack. For a unary operator it will only change the top element, but a
12411 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012412static int
12413arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012414{
Eric Andersen90898442003-08-06 11:20:52 +000012415 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012416 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012417 int ret_arith_lookup_val;
12418
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012419 /* There is no operator that can work without arguments */
12420 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012421 numptr_m1 = NUMPTR - 1;
12422
12423 /* check operand is var with noninteger value */
12424 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012425 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012426 return ret_arith_lookup_val;
12427
12428 rez = numptr_m1->val;
12429 if (op == TOK_UMINUS)
12430 rez *= -1;
12431 else if (op == TOK_NOT)
12432 rez = !rez;
12433 else if (op == TOK_BNOT)
12434 rez = ~rez;
12435 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12436 rez++;
12437 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12438 rez--;
12439 else if (op != TOK_UPLUS) {
12440 /* Binary operators */
12441
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012442 /* check and binary operators need two arguments */
12443 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012444
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 /* ... and they pop one */
12446 --NUMPTR;
12447 numptr_val = rez;
12448 if (op == TOK_CONDITIONAL) {
12449 if (! numptr_m1->contidional_second_val_initialized) {
12450 /* protect $((expr1 ? expr2)) without ": expr" */
12451 goto err;
12452 }
12453 rez = numptr_m1->contidional_second_val;
12454 } else if (numptr_m1->contidional_second_val_initialized) {
12455 /* protect $((expr1 : expr2)) without "expr ? " */
12456 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012457 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012458 numptr_m1 = NUMPTR - 1;
12459 if (op != TOK_ASSIGN) {
12460 /* check operand is var with noninteger value for not '=' */
12461 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12462 if (ret_arith_lookup_val)
12463 return ret_arith_lookup_val;
12464 }
12465 if (op == TOK_CONDITIONAL) {
12466 numptr_m1->contidional_second_val = rez;
12467 }
12468 rez = numptr_m1->val;
12469 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012470 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012471 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012472 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012473 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012474 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012475 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012476 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012477 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012478 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012479 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012480 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012481 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012482 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012483 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012484 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012485 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012486 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012487 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012488 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012489 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012490 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012491 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012492 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012493 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012494 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012495 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012496 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012497 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012498 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012499 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012500 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012501 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012502 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012503 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012504 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012505 /* protect $((expr : expr)) without "expr ? " */
12506 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012507 }
12508 numptr_m1->contidional_second_val_initialized = op;
12509 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012510 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012511 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012512 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012513 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012514 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012515 return -3; /* exponent less than 0 */
12516 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012517 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012518
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012519 if (numptr_val)
12520 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012521 c *= rez;
12522 rez = c;
12523 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012524 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012525 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012526 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012527 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012528 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012529 rez %= numptr_val;
12530 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012531 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012532 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012533
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012534 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012535 /* Hmm, 1=2 ? */
12536 goto err;
12537 }
12538 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012539#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012540 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012541#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012542 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012543#endif
Eric Andersen90898442003-08-06 11:20:52 +000012544 setvar(numptr_m1->var, buf, 0);
12545 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012546 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012547 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012548 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012549 rez++;
12550 }
12551 numptr_m1->val = rez;
12552 /* protect geting var value, is number now */
12553 numptr_m1->var = NULL;
12554 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012555 err:
12556 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012557}
12558
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012559/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012560static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012561 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12562 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12563 '<','<', 0, TOK_LSHIFT,
12564 '>','>', 0, TOK_RSHIFT,
12565 '|','|', 0, TOK_OR,
12566 '&','&', 0, TOK_AND,
12567 '!','=', 0, TOK_NE,
12568 '<','=', 0, TOK_LE,
12569 '>','=', 0, TOK_GE,
12570 '=','=', 0, TOK_EQ,
12571 '|','=', 0, TOK_OR_ASSIGN,
12572 '&','=', 0, TOK_AND_ASSIGN,
12573 '*','=', 0, TOK_MUL_ASSIGN,
12574 '/','=', 0, TOK_DIV_ASSIGN,
12575 '%','=', 0, TOK_REM_ASSIGN,
12576 '+','=', 0, TOK_PLUS_ASSIGN,
12577 '-','=', 0, TOK_MINUS_ASSIGN,
12578 '-','-', 0, TOK_POST_DEC,
12579 '^','=', 0, TOK_XOR_ASSIGN,
12580 '+','+', 0, TOK_POST_INC,
12581 '*','*', 0, TOK_EXPONENT,
12582 '!', 0, TOK_NOT,
12583 '<', 0, TOK_LT,
12584 '>', 0, TOK_GT,
12585 '=', 0, TOK_ASSIGN,
12586 '|', 0, TOK_BOR,
12587 '&', 0, TOK_BAND,
12588 '*', 0, TOK_MUL,
12589 '/', 0, TOK_DIV,
12590 '%', 0, TOK_REM,
12591 '+', 0, TOK_ADD,
12592 '-', 0, TOK_SUB,
12593 '^', 0, TOK_BXOR,
12594 /* uniq */
12595 '~', 0, TOK_BNOT,
12596 ',', 0, TOK_COMMA,
12597 '?', 0, TOK_CONDITIONAL,
12598 ':', 0, TOK_CONDITIONAL_SEP,
12599 ')', 0, TOK_RPAREN,
12600 '(', 0, TOK_LPAREN,
12601 0
12602};
12603/* ptr to ")" */
12604#define endexpression &op_tokens[sizeof(op_tokens)-7]
12605
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012606static arith_t
12607arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012608{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012609 char arithval; /* Current character under analysis */
12610 operator lasttok, op;
12611 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012612
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012613 const char *p = endexpression;
12614 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012615
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012616 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012617
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012618 /* Stack of integers */
12619 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12620 * in any given correct or incorrect expression is left as an exercise to
12621 * the reader. */
12622 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12623 *numstackptr = numstack;
12624 /* Stack of operator tokens */
12625 operator *stack = alloca((datasizes) * sizeof(operator)),
12626 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012627
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012628 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12629 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012630
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012631 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012632 arithval = *expr;
12633 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012634 if (p == endexpression) {
12635 /* Null expression. */
12636 return 0;
12637 }
12638
12639 /* This is only reached after all tokens have been extracted from the
12640 * input stream. If there are still tokens on the operator stack, they
12641 * are to be applied in order. At the end, there should be a final
12642 * result on the integer stack */
12643
12644 if (expr != endexpression + 1) {
12645 /* If we haven't done so already, */
12646 /* append a closing right paren */
12647 expr = endexpression;
12648 /* and let the loop process it. */
12649 continue;
12650 }
12651 /* At this point, we're done with the expression. */
12652 if (numstackptr != numstack+1) {
12653 /* ... but if there isn't, it's bad */
12654 err:
12655 return (*perrcode = -1);
12656 }
12657 if (numstack->var) {
12658 /* expression is $((var)) only, lookup now */
12659 errcode = arith_lookup_val(numstack);
12660 }
12661 ret:
12662 *perrcode = errcode;
12663 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012664 }
12665
Eric Andersen90898442003-08-06 11:20:52 +000012666 /* Continue processing the expression. */
12667 if (arith_isspace(arithval)) {
12668 /* Skip whitespace */
12669 goto prologue;
12670 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012671 p = endofname(expr);
12672 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012673 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012674
12675 numstackptr->var = alloca(var_name_size);
12676 safe_strncpy(numstackptr->var, expr, var_name_size);
12677 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012678 num:
Eric Andersen90898442003-08-06 11:20:52 +000012679 numstackptr->contidional_second_val_initialized = 0;
12680 numstackptr++;
12681 lasttok = TOK_NUM;
12682 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012683 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012684 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012685 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012686#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012687 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012688#else
12689 numstackptr->val = strtol(expr, (char **) &expr, 0);
12690#endif
Eric Andersen90898442003-08-06 11:20:52 +000012691 goto num;
12692 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012693 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012694 const char *o;
12695
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012696 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012697 /* strange operator not found */
12698 goto err;
12699 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012700 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012701 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012702 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012703 /* found */
12704 expr = o - 1;
12705 break;
12706 }
12707 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012708 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012709 p++;
12710 /* skip zero delim */
12711 p++;
12712 }
12713 op = p[1];
12714
12715 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012716 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12717 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012718
12719 /* Plus and minus are binary (not unary) _only_ if the last
12720 * token was as number, or a right paren (which pretends to be
12721 * a number, since it evaluates to one). Think about it.
12722 * It makes sense. */
12723 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012724 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012725 case TOK_ADD:
12726 op = TOK_UPLUS;
12727 break;
12728 case TOK_SUB:
12729 op = TOK_UMINUS;
12730 break;
12731 case TOK_POST_INC:
12732 op = TOK_PRE_INC;
12733 break;
12734 case TOK_POST_DEC:
12735 op = TOK_PRE_DEC;
12736 break;
Eric Andersen90898442003-08-06 11:20:52 +000012737 }
12738 }
12739 /* We don't want a unary operator to cause recursive descent on the
12740 * stack, because there can be many in a row and it could cause an
12741 * operator to be evaluated before its argument is pushed onto the
12742 * integer stack. */
12743 /* But for binary operators, "apply" everything on the operator
12744 * stack until we find an operator with a lesser priority than the
12745 * one we have just extracted. */
12746 /* Left paren is given the lowest priority so it will never be
12747 * "applied" in this way.
12748 * if associativity is right and priority eq, applied also skip
12749 */
12750 prec = PREC(op);
12751 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12752 /* not left paren or unary */
12753 if (lasttok != TOK_NUM) {
12754 /* binary op must be preceded by a num */
12755 goto err;
12756 }
12757 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012758 if (op == TOK_RPAREN) {
12759 /* The algorithm employed here is simple: while we don't
12760 * hit an open paren nor the bottom of the stack, pop
12761 * tokens and apply them */
12762 if (stackptr[-1] == TOK_LPAREN) {
12763 --stackptr;
12764 /* Any operator directly after a */
12765 lasttok = TOK_NUM;
12766 /* close paren should consider itself binary */
12767 goto prologue;
12768 }
12769 } else {
12770 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012771
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012772 convert_prec_is_assing(prec);
12773 convert_prec_is_assing(prev_prec);
12774 if (prev_prec < prec)
12775 break;
12776 /* check right assoc */
12777 if (prev_prec == prec && is_right_associativity(prec))
12778 break;
12779 }
12780 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12781 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012782 }
12783 if (op == TOK_RPAREN) {
12784 goto err;
12785 }
12786 }
12787
12788 /* Push this operator to the stack and remember it. */
12789 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012790 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012791 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012792 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012793}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012794#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012795
12796
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012797/* ============ main() and helpers */
12798
12799/*
12800 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012801 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012802static void exitshell(void) ATTRIBUTE_NORETURN;
12803static void
12804exitshell(void)
12805{
12806 struct jmploc loc;
12807 char *p;
12808 int status;
12809
12810 status = exitstatus;
12811 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12812 if (setjmp(loc.loc)) {
12813 if (exception == EXEXIT)
12814/* dash bug: it just does _exit(exitstatus) here
12815 * but we have to do setjobctl(0) first!
12816 * (bug is still not fixed in dash-0.5.3 - if you run dash
12817 * under Midnight Commander, on exit from dash MC is backgrounded) */
12818 status = exitstatus;
12819 goto out;
12820 }
12821 exception_handler = &loc;
12822 p = trap[0];
12823 if (p) {
12824 trap[0] = NULL;
12825 evalstring(p, 0);
12826 }
12827 flush_stdout_stderr();
12828 out:
12829 setjobctl(0);
12830 _exit(status);
12831 /* NOTREACHED */
12832}
12833
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012834static void
12835init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012836{
12837 /* from input.c: */
12838 basepf.nextc = basepf.buf = basebuf;
12839
12840 /* from trap.c: */
12841 signal(SIGCHLD, SIG_DFL);
12842
12843 /* from var.c: */
12844 {
12845 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012846 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012847 const char *p;
12848 struct stat st1, st2;
12849
12850 initvar();
12851 for (envp = environ; envp && *envp; envp++) {
12852 if (strchr(*envp, '=')) {
12853 setvareq(*envp, VEXPORT|VTEXTFIXED);
12854 }
12855 }
12856
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012857 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012858 setvar("PPID", ppid, 0);
12859
12860 p = lookupvar("PWD");
12861 if (p)
12862 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12863 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12864 p = '\0';
12865 setpwd(p, 0);
12866 }
12867}
12868
12869/*
12870 * Process the shell command line arguments.
12871 */
12872static void
12873procargs(int argc, char **argv)
12874{
12875 int i;
12876 const char *xminusc;
12877 char **xargv;
12878
12879 xargv = argv;
12880 arg0 = xargv[0];
12881 if (argc > 0)
12882 xargv++;
12883 for (i = 0; i < NOPTS; i++)
12884 optlist[i] = 2;
12885 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012886 if (options(1)) {
12887 /* it already printed err message */
12888 raise_exception(EXERROR);
12889 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012890 xargv = argptr;
12891 xminusc = minusc;
12892 if (*xargv == NULL) {
12893 if (xminusc)
12894 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12895 sflag = 1;
12896 }
12897 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12898 iflag = 1;
12899 if (mflag == 2)
12900 mflag = iflag;
12901 for (i = 0; i < NOPTS; i++)
12902 if (optlist[i] == 2)
12903 optlist[i] = 0;
12904#if DEBUG == 2
12905 debug = 1;
12906#endif
12907 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12908 if (xminusc) {
12909 minusc = *xargv++;
12910 if (*xargv)
12911 goto setarg0;
12912 } else if (!sflag) {
12913 setinputfile(*xargv, 0);
12914 setarg0:
12915 arg0 = *xargv++;
12916 commandname = arg0;
12917 }
12918
12919 shellparam.p = xargv;
12920#if ENABLE_ASH_GETOPTS
12921 shellparam.optind = 1;
12922 shellparam.optoff = -1;
12923#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012924 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012925 while (*xargv) {
12926 shellparam.nparam++;
12927 xargv++;
12928 }
12929 optschanged();
12930}
12931
12932/*
12933 * Read /etc/profile or .profile.
12934 */
12935static void
12936read_profile(const char *name)
12937{
12938 int skip;
12939
12940 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12941 return;
12942 skip = cmdloop(0);
12943 popfile();
12944 if (skip)
12945 exitshell();
12946}
12947
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012948/*
12949 * This routine is called when an error or an interrupt occurs in an
12950 * interactive shell and control is returned to the main command loop.
12951 */
12952static void
12953reset(void)
12954{
12955 /* from eval.c: */
12956 evalskip = 0;
12957 loopnest = 0;
12958 /* from input.c: */
12959 parselleft = parsenleft = 0; /* clear input buffer */
12960 popallfiles();
12961 /* from parser.c: */
12962 tokpushback = 0;
12963 checkkwd = 0;
12964 /* from redir.c: */
12965 clearredir(0);
12966}
12967
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012968#if PROFILE
12969static short profile_buf[16384];
12970extern int etext();
12971#endif
12972
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012973/*
12974 * Main routine. We initialize things, parse the arguments, execute
12975 * profiles if we're a login shell, and then call cmdloop to execute
12976 * commands. The setjmp call sets up the location to jump to when an
12977 * exception occurs. When an exception occurs the variable "state"
12978 * is used to figure out how far we had gotten.
12979 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012980int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012981int ash_main(int argc, char **argv)
12982{
12983 char *shinit;
12984 volatile int state;
12985 struct jmploc jmploc;
12986 struct stackmark smark;
12987
Denis Vlasenko01631112007-12-16 17:20:38 +000012988 /* Initialize global data */
12989 INIT_G_misc();
12990 INIT_G_memstack();
12991 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012992#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012993 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012994#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012995 INIT_G_cmdtable();
12996
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012997#if PROFILE
12998 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12999#endif
13000
13001#if ENABLE_FEATURE_EDITING
13002 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13003#endif
13004 state = 0;
13005 if (setjmp(jmploc.loc)) {
13006 int e;
13007 int s;
13008
13009 reset();
13010
13011 e = exception;
13012 if (e == EXERROR)
13013 exitstatus = 2;
13014 s = state;
13015 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13016 exitshell();
13017
13018 if (e == EXINT) {
13019 outcslow('\n', stderr);
13020 }
13021 popstackmark(&smark);
13022 FORCE_INT_ON; /* enable interrupts */
13023 if (s == 1)
13024 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013025 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013026 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013027 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013028 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013029 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013030 }
13031 exception_handler = &jmploc;
13032#if DEBUG
13033 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013034 trace_puts("Shell args: ");
13035 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013036#endif
13037 rootpid = getpid();
13038
13039#if ENABLE_ASH_RANDOM_SUPPORT
13040 rseed = rootpid + time(NULL);
13041#endif
13042 init();
13043 setstackmark(&smark);
13044 procargs(argc, argv);
13045#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13046 if (iflag) {
13047 const char *hp = lookupvar("HISTFILE");
13048
13049 if (hp == NULL) {
13050 hp = lookupvar("HOME");
13051 if (hp != NULL) {
13052 char *defhp = concat_path_file(hp, ".ash_history");
13053 setvar("HISTFILE", defhp, 0);
13054 free(defhp);
13055 }
13056 }
13057 }
13058#endif
13059 if (argv[0] && argv[0][0] == '-')
13060 isloginsh = 1;
13061 if (isloginsh) {
13062 state = 1;
13063 read_profile("/etc/profile");
13064 state1:
13065 state = 2;
13066 read_profile(".profile");
13067 }
13068 state2:
13069 state = 3;
13070 if (
13071#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013072 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013073#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013074 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013075 ) {
13076 shinit = lookupvar("ENV");
13077 if (shinit != NULL && *shinit != '\0') {
13078 read_profile(shinit);
13079 }
13080 }
13081 state3:
13082 state = 4;
13083 if (minusc)
13084 evalstring(minusc, 0);
13085
13086 if (sflag || minusc == NULL) {
13087#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13088 if ( iflag ) {
13089 const char *hp = lookupvar("HISTFILE");
13090
13091 if (hp != NULL)
13092 line_input_state->hist_file = hp;
13093 }
13094#endif
13095 state4: /* XXX ??? - why isn't this before the "if" statement */
13096 cmdloop(1);
13097 }
13098#if PROFILE
13099 monitor(0);
13100#endif
13101#ifdef GPROF
13102 {
13103 extern void _mcleanup(void);
13104 _mcleanup();
13105 }
13106#endif
13107 exitshell();
13108 /* NOTREACHED */
13109}
13110
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013111#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013112const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013113int main(int argc, char **argv)
13114{
13115 return ash_main(argc, argv);
13116}
13117#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013118
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013119
Eric Andersendf82f612001-06-28 07:46:40 +000013120/*-
13121 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013122 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013123 *
13124 * This code is derived from software contributed to Berkeley by
13125 * Kenneth Almquist.
13126 *
13127 * Redistribution and use in source and binary forms, with or without
13128 * modification, are permitted provided that the following conditions
13129 * are met:
13130 * 1. Redistributions of source code must retain the above copyright
13131 * notice, this list of conditions and the following disclaimer.
13132 * 2. Redistributions in binary form must reproduce the above copyright
13133 * notice, this list of conditions and the following disclaimer in the
13134 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013135 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013136 * may be used to endorse or promote products derived from this software
13137 * without specific prior written permission.
13138 *
13139 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13140 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13141 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13142 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13143 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13144 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13145 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13146 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13147 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13148 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13149 * SUCH DAMAGE.
13150 */