blob: 668953c0a85dd54b1780c53d1db07eff4390d599 [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
Eric Andersencb57d552001-06-28 07:25:16 +000064
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};
205/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
206static struct globals_misc *const ptr_to_globals_misc __attribute__ ((section (".data")));
207#define G_misc (*ptr_to_globals_misc)
208#define rootpid (G_misc.rootpid )
209#define shlvl (G_misc.shlvl )
210#define minusc (G_misc.minusc )
211#define curdir (G_misc.curdir )
212#define physdir (G_misc.physdir )
213#define arg0 (G_misc.arg0 )
214#define exception_handler (G_misc.exception_handler)
215#define exception (G_misc.exception )
216#define suppressint (G_misc.suppressint )
217#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000218//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000219#define pendingsig (G_misc.pendingsig )
220#define trap (G_misc.trap )
221#define isloginsh (G_misc.isloginsh)
222#define nullstr (G_misc.nullstr )
223#define sigmode (G_misc.sigmode )
224#define gotsig (G_misc.gotsig )
225#define INIT_G_misc() do { \
226 (*(struct globals_misc**)&ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
227 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};
1150/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1151static struct globals_memstack *const ptr_to_globals_memstack __attribute__ ((section (".data")));
1152#define G_memstack (*ptr_to_globals_memstack)
1153#define g_stackp (G_memstack.g_stackp )
1154#define markp (G_memstack.markp )
1155#define g_stacknxt (G_memstack.g_stacknxt )
1156#define sstrend (G_memstack.sstrend )
1157#define g_stacknleft (G_memstack.g_stacknleft)
1158#define herefd (G_memstack.herefd )
1159#define stackbase (G_memstack.stackbase )
1160#define INIT_G_memstack() do { \
1161 (*(struct globals_memstack**)&ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1162 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
1188/*
1189 * Make a copy of a string in safe storage.
1190 */
1191static char *
1192ckstrdup(const char *s)
1193{
1194 char *p = strdup(s);
1195 if (!p)
1196 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1197 return p;
1198}
1199
1200/*
1201 * Parse trees for commands are allocated in lifo order, so we use a stack
1202 * to make this more efficient, and also to avoid all sorts of exception
1203 * handling code to handle interrupts in the middle of a parse.
1204 *
1205 * The size 504 was chosen because the Ultrix malloc handles that size
1206 * well.
1207 */
1208static void *
1209stalloc(size_t nbytes)
1210{
1211 char *p;
1212 size_t aligned;
1213
1214 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001215 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001216 size_t len;
1217 size_t blocksize;
1218 struct stack_block *sp;
1219
1220 blocksize = aligned;
1221 if (blocksize < MINSIZE)
1222 blocksize = MINSIZE;
1223 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1224 if (len < blocksize)
1225 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1226 INT_OFF;
1227 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001228 sp->prev = g_stackp;
1229 g_stacknxt = sp->space;
1230 g_stacknleft = blocksize;
1231 sstrend = g_stacknxt + blocksize;
1232 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001233 INT_ON;
1234 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001235 p = g_stacknxt;
1236 g_stacknxt += aligned;
1237 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001238 return p;
1239}
1240
1241static void
1242stunalloc(void *p)
1243{
1244#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001245 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001246 write(2, "stunalloc\n", 10);
1247 abort();
1248 }
1249#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001250 g_stacknleft += g_stacknxt - (char *)p;
1251 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001252}
1253
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001254/*
1255 * Like strdup but works with the ash stack.
1256 */
1257static char *
1258ststrdup(const char *p)
1259{
1260 size_t len = strlen(p) + 1;
1261 return memcpy(stalloc(len), p, len);
1262}
1263
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001264static void
1265setstackmark(struct stackmark *mark)
1266{
Denis Vlasenko01631112007-12-16 17:20:38 +00001267 mark->stackp = g_stackp;
1268 mark->stacknxt = g_stacknxt;
1269 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001270 mark->marknext = markp;
1271 markp = mark;
1272}
1273
1274static void
1275popstackmark(struct stackmark *mark)
1276{
1277 struct stack_block *sp;
1278
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001279 if (!mark->stackp)
1280 return;
1281
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001282 INT_OFF;
1283 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001284 while (g_stackp != mark->stackp) {
1285 sp = g_stackp;
1286 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001287 free(sp);
1288 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001289 g_stacknxt = mark->stacknxt;
1290 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001291 sstrend = mark->stacknxt + mark->stacknleft;
1292 INT_ON;
1293}
1294
1295/*
1296 * When the parser reads in a string, it wants to stick the string on the
1297 * stack and only adjust the stack pointer when it knows how big the
1298 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1299 * of space on top of the stack and stackblocklen returns the length of
1300 * this block. Growstackblock will grow this space by at least one byte,
1301 * possibly moving it (like realloc). Grabstackblock actually allocates the
1302 * part of the block that has been used.
1303 */
1304static void
1305growstackblock(void)
1306{
1307 size_t newlen;
1308
Denis Vlasenko01631112007-12-16 17:20:38 +00001309 newlen = g_stacknleft * 2;
1310 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001311 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1312 if (newlen < 128)
1313 newlen += 128;
1314
Denis Vlasenko01631112007-12-16 17:20:38 +00001315 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001316 struct stack_block *oldstackp;
1317 struct stackmark *xmark;
1318 struct stack_block *sp;
1319 struct stack_block *prevstackp;
1320 size_t grosslen;
1321
1322 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001323 oldstackp = g_stackp;
1324 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001325 prevstackp = sp->prev;
1326 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1327 sp = ckrealloc(sp, grosslen);
1328 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001329 g_stackp = sp;
1330 g_stacknxt = sp->space;
1331 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001332 sstrend = sp->space + newlen;
1333
1334 /*
1335 * Stack marks pointing to the start of the old block
1336 * must be relocated to point to the new block
1337 */
1338 xmark = markp;
1339 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001340 xmark->stackp = g_stackp;
1341 xmark->stacknxt = g_stacknxt;
1342 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343 xmark = xmark->marknext;
1344 }
1345 INT_ON;
1346 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001347 char *oldspace = g_stacknxt;
1348 int oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001349 char *p = stalloc(newlen);
1350
1351 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001352 g_stacknxt = memcpy(p, oldspace, oldlen);
1353 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001354 }
1355}
1356
1357static void
1358grabstackblock(size_t len)
1359{
1360 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001361 g_stacknxt += len;
1362 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001363}
1364
1365/*
1366 * The following routines are somewhat easier to use than the above.
1367 * The user declares a variable of type STACKSTR, which may be declared
1368 * to be a register. The macro STARTSTACKSTR initializes things. Then
1369 * the user uses the macro STPUTC to add characters to the string. In
1370 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1371 * grown as necessary. When the user is done, she can just leave the
1372 * string there and refer to it using stackblock(). Or she can allocate
1373 * the space for it using grabstackstr(). If it is necessary to allow
1374 * someone else to use the stack temporarily and then continue to grow
1375 * the string, the user should use grabstack to allocate the space, and
1376 * then call ungrabstr(p) to return to the previous mode of operation.
1377 *
1378 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1379 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1380 * is space for at least one character.
1381 */
1382static void *
1383growstackstr(void)
1384{
1385 size_t len = stackblocksize();
1386 if (herefd >= 0 && len >= 1024) {
1387 full_write(herefd, stackblock(), len);
1388 return stackblock();
1389 }
1390 growstackblock();
1391 return stackblock() + len;
1392}
1393
1394/*
1395 * Called from CHECKSTRSPACE.
1396 */
1397static char *
1398makestrspace(size_t newlen, char *p)
1399{
Denis Vlasenko01631112007-12-16 17:20:38 +00001400 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001401 size_t size = stackblocksize();
1402
1403 for (;;) {
1404 size_t nleft;
1405
1406 size = stackblocksize();
1407 nleft = size - len;
1408 if (nleft >= newlen)
1409 break;
1410 growstackblock();
1411 }
1412 return stackblock() + len;
1413}
1414
1415static char *
1416stack_nputstr(const char *s, size_t n, char *p)
1417{
1418 p = makestrspace(n, p);
1419 p = memcpy(p, s, n) + n;
1420 return p;
1421}
1422
1423static char *
1424stack_putstr(const char *s, char *p)
1425{
1426 return stack_nputstr(s, strlen(s), p);
1427}
1428
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001429static char *
1430_STPUTC(int c, char *p)
1431{
1432 if (p == sstrend)
1433 p = growstackstr();
1434 *p++ = c;
1435 return p;
1436}
1437
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001438#define STARTSTACKSTR(p) ((p) = stackblock())
1439#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001440#define CHECKSTRSPACE(n, p) \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001441 do { \
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001442 char *q = (p); \
1443 size_t l = (n); \
1444 size_t m = sstrend - q; \
1445 if (l > m) \
1446 (p) = makestrspace(l, q); \
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001447 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001448#define USTPUTC(c, p) (*p++ = (c))
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001449#define STACKSTRNUL(p) \
1450 do { \
1451 if ((p) == sstrend) \
1452 p = growstackstr(); \
1453 *p = '\0'; \
1454 } while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001455#define STUNPUTC(p) (--p)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001456#define STTOPC(p) (p[-1])
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001457#define STADJUST(amount, p) (p += (amount))
1458
1459#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1460#define ungrabstackstr(s, p) stunalloc((s))
1461#define stackstrend() ((void *)sstrend)
1462
1463
1464/* ============ String helpers */
1465
1466/*
1467 * prefix -- see if pfx is a prefix of string.
1468 */
1469static char *
1470prefix(const char *string, const char *pfx)
1471{
1472 while (*pfx) {
1473 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001474 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001475 }
1476 return (char *) string;
1477}
1478
1479/*
1480 * Check for a valid number. This should be elsewhere.
1481 */
1482static int
1483is_number(const char *p)
1484{
1485 do {
1486 if (!isdigit(*p))
1487 return 0;
1488 } while (*++p != '\0');
1489 return 1;
1490}
1491
1492/*
1493 * Convert a string of digits to an integer, printing an error message on
1494 * failure.
1495 */
1496static int
1497number(const char *s)
1498{
1499 if (!is_number(s))
1500 ash_msg_and_raise_error(illnum, s);
1501 return atoi(s);
1502}
1503
1504/*
1505 * Produce a possibly single quoted string suitable as input to the shell.
1506 * The return string is allocated on the stack.
1507 */
1508static char *
1509single_quote(const char *s)
1510{
1511 char *p;
1512
1513 STARTSTACKSTR(p);
1514
1515 do {
1516 char *q;
1517 size_t len;
1518
1519 len = strchrnul(s, '\'') - s;
1520
1521 q = p = makestrspace(len + 3, p);
1522
1523 *q++ = '\'';
1524 q = memcpy(q, s, len) + len;
1525 *q++ = '\'';
1526 s += len;
1527
1528 STADJUST(q - p, p);
1529
1530 len = strspn(s, "'");
1531 if (!len)
1532 break;
1533
1534 q = p = makestrspace(len + 3, p);
1535
1536 *q++ = '"';
1537 q = memcpy(q, s, len) + len;
1538 *q++ = '"';
1539 s += len;
1540
1541 STADJUST(q - p, p);
1542 } while (*s);
1543
1544 USTPUTC(0, p);
1545
1546 return stackblock();
1547}
1548
1549
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001550/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001551
1552static char **argptr; /* argument list for builtin commands */
1553static char *optionarg; /* set by nextopt (like getopt) */
1554static char *optptr; /* used by nextopt */
1555
1556/*
1557 * XXX - should get rid of. have all builtins use getopt(3). the
1558 * library getopt must have the BSD extension static variable "optreset"
1559 * otherwise it can't be used within the shell safely.
1560 *
1561 * Standard option processing (a la getopt) for builtin routines. The
1562 * only argument that is passed to nextopt is the option string; the
1563 * other arguments are unnecessary. It return the character, or '\0' on
1564 * end of input.
1565 */
1566static int
1567nextopt(const char *optstring)
1568{
1569 char *p;
1570 const char *q;
1571 char c;
1572
1573 p = optptr;
1574 if (p == NULL || *p == '\0') {
1575 p = *argptr;
1576 if (p == NULL || *p != '-' || *++p == '\0')
1577 return '\0';
1578 argptr++;
1579 if (LONE_DASH(p)) /* check for "--" */
1580 return '\0';
1581 }
1582 c = *p++;
1583 for (q = optstring; *q != c; ) {
1584 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001585 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001586 if (*++q == ':')
1587 q++;
1588 }
1589 if (*++q == ':') {
1590 if (*p == '\0' && (p = *argptr++) == NULL)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001591 ash_msg_and_raise_error("no arg for -%c option", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001592 optionarg = p;
1593 p = NULL;
1594 }
1595 optptr = p;
1596 return c;
1597}
1598
1599
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001600/* ============ Math support definitions */
1601
1602#if ENABLE_ASH_MATH_SUPPORT_64
1603typedef int64_t arith_t;
1604#define arith_t_type long long
1605#else
1606typedef long arith_t;
1607#define arith_t_type long
1608#endif
1609
1610#if ENABLE_ASH_MATH_SUPPORT
1611static arith_t dash_arith(const char *);
1612static arith_t arith(const char *expr, int *perrcode);
1613#endif
1614
1615#if ENABLE_ASH_RANDOM_SUPPORT
1616static unsigned long rseed;
1617#ifndef DYNAMIC_VAR
1618#define DYNAMIC_VAR
1619#endif
1620#endif
1621
1622
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001623/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001624
Denis Vlasenko01631112007-12-16 17:20:38 +00001625/*
1626 * The parsefile structure pointed to by the global variable parsefile
1627 * contains information about the current file being read.
1628 */
1629struct redirtab {
1630 struct redirtab *next;
1631 int renamed[10];
1632 int nullredirs;
1633};
1634
1635struct shparam {
1636 int nparam; /* # of positional parameters (without $0) */
1637#if ENABLE_ASH_GETOPTS
1638 int optind; /* next parameter to be processed by getopts */
1639 int optoff; /* used by getopts */
1640#endif
1641 unsigned char malloced; /* if parameter list dynamically allocated */
1642 char **p; /* parameter list */
1643};
1644
1645/*
1646 * Free the list of positional parameters.
1647 */
1648static void
1649freeparam(volatile struct shparam *param)
1650{
1651 char **ap;
1652
1653 if (param->malloced) {
1654 for (ap = param->p; *ap; ap++)
1655 free(*ap);
1656 free(param->p);
1657 }
1658}
1659
1660#if ENABLE_ASH_GETOPTS
1661static void getoptsreset(const char *value);
1662#endif
1663
1664struct var {
1665 struct var *next; /* next entry in hash list */
1666 int flags; /* flags are defined above */
1667 const char *text; /* name=value */
1668 void (*func)(const char *); /* function to be called when */
1669 /* the variable gets set/unset */
1670};
1671
1672struct localvar {
1673 struct localvar *next; /* next local variable in list */
1674 struct var *vp; /* the variable that was made local */
1675 int flags; /* saved flags */
1676 const char *text; /* saved text */
1677};
1678
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001679/* flags */
1680#define VEXPORT 0x01 /* variable is exported */
1681#define VREADONLY 0x02 /* variable cannot be modified */
1682#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1683#define VTEXTFIXED 0x08 /* text is statically allocated */
1684#define VSTACK 0x10 /* text is allocated on the stack */
1685#define VUNSET 0x20 /* the variable is not set */
1686#define VNOFUNC 0x40 /* don't call the callback function */
1687#define VNOSET 0x80 /* do not set variable - just readonly test */
1688#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1689#ifdef DYNAMIC_VAR
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001690# define VDYNAMIC 0x200 /* dynamic variable */
1691#else
1692# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001693#endif
1694
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001695#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001696static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001697#define defifs (defifsvar + 4)
1698#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001699static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001700#endif
1701
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001702
Denis Vlasenko01631112007-12-16 17:20:38 +00001703/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001704#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001705static void
1706change_lc_all(const char *value)
1707{
1708 if (value && *value != '\0')
1709 setlocale(LC_ALL, value);
1710}
1711static void
1712change_lc_ctype(const char *value)
1713{
1714 if (value && *value != '\0')
1715 setlocale(LC_CTYPE, value);
1716}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001717#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001718#if ENABLE_ASH_MAIL
1719static void chkmail(void);
1720static void changemail(const char *);
1721#endif
1722static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723#if ENABLE_ASH_RANDOM_SUPPORT
1724static void change_random(const char *);
1725#endif
1726
Denis Vlasenko01631112007-12-16 17:20:38 +00001727static const struct {
1728 int flags;
1729 const char *text;
1730 void (*func)(const char *);
1731} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001733 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001735 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001736#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001738 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1739 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001741 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1742 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1743 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1744 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001746 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001747#endif
1748#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750#endif
1751#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001752 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1753 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754#endif
1755#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001756 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001757#endif
1758};
1759
Denis Vlasenko01631112007-12-16 17:20:38 +00001760
1761struct globals_var {
1762 struct shparam shellparam; /* $@ current positional parameters */
1763 struct redirtab *redirlist;
1764 int g_nullredirs;
1765 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1766 struct var *vartab[VTABSIZE];
1767 struct var varinit[ARRAY_SIZE(varinit_data)];
1768};
1769/* Make it reside in writable memory, yet make compiler understand that it is not going to change. */
1770static struct globals_var *const ptr_to_globals_var __attribute__ ((section (".data")));
1771#define G_var (*ptr_to_globals_var)
1772#define shellparam (G_var.shellparam )
1773#define redirlist (G_var.redirlist )
1774#define g_nullredirs (G_var.g_nullredirs )
1775#define preverrout_fd (G_var.preverrout_fd)
1776#define vartab (G_var.vartab )
1777#define varinit (G_var.varinit )
1778#define INIT_G_var() do { \
1779 int i; \
1780 (*(struct globals_var**)&ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1781 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1782 varinit[i].flags = varinit_data[i].flags; \
1783 varinit[i].text = varinit_data[i].text; \
1784 varinit[i].func = varinit_data[i].func; \
1785 } \
1786} while (0)
1787
1788#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001789#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001790#define vmail (&vifs)[1]
1791#define vmpath (&vmail)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001792#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001793#define vmpath vifs
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001794#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001795#define vpath (&vmpath)[1]
1796#define vps1 (&vpath)[1]
1797#define vps2 (&vps1)[1]
1798#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001799#define voptind (&vps4)[1]
1800#if ENABLE_ASH_GETOPTS
1801#define vrandom (&voptind)[1]
1802#else
1803#define vrandom (&vps4)[1]
1804#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805
1806/*
1807 * The following macros access the values of the above variables.
1808 * They have to skip over the name. They return the null string
1809 * for unset variables.
1810 */
1811#define ifsval() (vifs.text + 4)
1812#define ifsset() ((vifs.flags & VUNSET) == 0)
1813#define mailval() (vmail.text + 5)
1814#define mpathval() (vmpath.text + 9)
1815#define pathval() (vpath.text + 5)
1816#define ps1val() (vps1.text + 4)
1817#define ps2val() (vps2.text + 4)
1818#define ps4val() (vps4.text + 4)
1819#define optindval() (voptind.text + 7)
1820
1821#define mpathset() ((vmpath.flags & VUNSET) == 0)
1822
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001823
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001824#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1825#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1826
Denis Vlasenko01631112007-12-16 17:20:38 +00001827#if ENABLE_ASH_GETOPTS
1828static void
1829getoptsreset(const char *value)
1830{
1831 shellparam.optind = number(value);
1832 shellparam.optoff = -1;
1833}
1834#endif
1835
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836/*
1837 * Return of a legal variable name (a letter or underscore followed by zero or
1838 * more letters, underscores, and digits).
1839 */
1840static char *
1841endofname(const char *name)
1842{
1843 char *p;
1844
1845 p = (char *) name;
1846 if (!is_name(*p))
1847 return p;
1848 while (*++p) {
1849 if (!is_in_name(*p))
1850 break;
1851 }
1852 return p;
1853}
1854
1855/*
1856 * Compares two strings up to the first = or '\0'. The first
1857 * string must be terminated by '='; the second may be terminated by
1858 * either '=' or '\0'.
1859 */
1860static int
1861varcmp(const char *p, const char *q)
1862{
1863 int c, d;
1864
1865 while ((c = *p) == (d = *q)) {
1866 if (!c || c == '=')
1867 goto out;
1868 p++;
1869 q++;
1870 }
1871 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001872 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001873 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001874 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001875 out:
1876 return c - d;
1877}
1878
1879static int
1880varequal(const char *a, const char *b)
1881{
1882 return !varcmp(a, b);
1883}
1884
1885/*
1886 * Find the appropriate entry in the hash table from the name.
1887 */
1888static struct var **
1889hashvar(const char *p)
1890{
1891 unsigned hashval;
1892
1893 hashval = ((unsigned char) *p) << 4;
1894 while (*p && *p != '=')
1895 hashval += (unsigned char) *p++;
1896 return &vartab[hashval % VTABSIZE];
1897}
1898
1899static int
1900vpcmp(const void *a, const void *b)
1901{
1902 return varcmp(*(const char **)a, *(const char **)b);
1903}
1904
1905/*
1906 * This routine initializes the builtin variables.
1907 */
1908static void
1909initvar(void)
1910{
1911 struct var *vp;
1912 struct var *end;
1913 struct var **vpp;
1914
1915 /*
1916 * PS1 depends on uid
1917 */
1918#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1919 vps1.text = "PS1=\\w \\$ ";
1920#else
1921 if (!geteuid())
1922 vps1.text = "PS1=# ";
1923#endif
1924 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001925 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001926 do {
1927 vpp = hashvar(vp->text);
1928 vp->next = *vpp;
1929 *vpp = vp;
1930 } while (++vp < end);
1931}
1932
1933static struct var **
1934findvar(struct var **vpp, const char *name)
1935{
1936 for (; *vpp; vpp = &(*vpp)->next) {
1937 if (varequal((*vpp)->text, name)) {
1938 break;
1939 }
1940 }
1941 return vpp;
1942}
1943
1944/*
1945 * Find the value of a variable. Returns NULL if not set.
1946 */
1947static char *
1948lookupvar(const char *name)
1949{
1950 struct var *v;
1951
1952 v = *findvar(hashvar(name), name);
1953 if (v) {
1954#ifdef DYNAMIC_VAR
1955 /*
1956 * Dynamic variables are implemented roughly the same way they are
1957 * in bash. Namely, they're "special" so long as they aren't unset.
1958 * As soon as they're unset, they're no longer dynamic, and dynamic
1959 * lookup will no longer happen at that point. -- PFM.
1960 */
1961 if ((v->flags & VDYNAMIC))
1962 (*v->func)(NULL);
1963#endif
1964 if (!(v->flags & VUNSET))
1965 return strchrnul(v->text, '=') + 1;
1966 }
1967 return NULL;
1968}
1969
1970/*
1971 * Search the environment of a builtin command.
1972 */
1973static char *
1974bltinlookup(const char *name)
1975{
1976 struct strlist *sp;
1977
1978 for (sp = cmdenviron; sp; sp = sp->next) {
1979 if (varequal(sp->text, name))
1980 return strchrnul(sp->text, '=') + 1;
1981 }
1982 return lookupvar(name);
1983}
1984
1985/*
1986 * Same as setvar except that the variable and value are passed in
1987 * the first argument as name=value. Since the first argument will
1988 * be actually stored in the table, it should not be a string that
1989 * will go away.
1990 * Called with interrupts off.
1991 */
1992static void
1993setvareq(char *s, int flags)
1994{
1995 struct var *vp, **vpp;
1996
1997 vpp = hashvar(s);
1998 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
1999 vp = *findvar(vpp, s);
2000 if (vp) {
2001 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2002 const char *n;
2003
2004 if (flags & VNOSAVE)
2005 free(s);
2006 n = vp->text;
2007 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2008 }
2009
2010 if (flags & VNOSET)
2011 return;
2012
2013 if (vp->func && (flags & VNOFUNC) == 0)
2014 (*vp->func)(strchrnul(s, '=') + 1);
2015
2016 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2017 free((char*)vp->text);
2018
2019 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2020 } else {
2021 if (flags & VNOSET)
2022 return;
2023 /* not found */
2024 vp = ckmalloc(sizeof(*vp));
2025 vp->next = *vpp;
2026 vp->func = NULL;
2027 *vpp = vp;
2028 }
2029 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2030 s = ckstrdup(s);
2031 vp->text = s;
2032 vp->flags = flags;
2033}
2034
2035/*
2036 * Set the value of a variable. The flags argument is ored with the
2037 * flags of the variable. If val is NULL, the variable is unset.
2038 */
2039static void
2040setvar(const char *name, const char *val, int flags)
2041{
2042 char *p, *q;
2043 size_t namelen;
2044 char *nameeq;
2045 size_t vallen;
2046
2047 q = endofname(name);
2048 p = strchrnul(q, '=');
2049 namelen = p - name;
2050 if (!namelen || p != q)
2051 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2052 vallen = 0;
2053 if (val == NULL) {
2054 flags |= VUNSET;
2055 } else {
2056 vallen = strlen(val);
2057 }
2058 INT_OFF;
2059 nameeq = ckmalloc(namelen + vallen + 2);
2060 p = memcpy(nameeq, name, namelen) + namelen;
2061 if (val) {
2062 *p++ = '=';
2063 p = memcpy(p, val, vallen) + vallen;
2064 }
2065 *p = '\0';
2066 setvareq(nameeq, flags | VNOSAVE);
2067 INT_ON;
2068}
2069
2070#if ENABLE_ASH_GETOPTS
2071/*
2072 * Safe version of setvar, returns 1 on success 0 on failure.
2073 */
2074static int
2075setvarsafe(const char *name, const char *val, int flags)
2076{
2077 int err;
2078 volatile int saveint;
2079 struct jmploc *volatile savehandler = exception_handler;
2080 struct jmploc jmploc;
2081
2082 SAVE_INT(saveint);
2083 if (setjmp(jmploc.loc))
2084 err = 1;
2085 else {
2086 exception_handler = &jmploc;
2087 setvar(name, val, flags);
2088 err = 0;
2089 }
2090 exception_handler = savehandler;
2091 RESTORE_INT(saveint);
2092 return err;
2093}
2094#endif
2095
2096/*
2097 * Unset the specified variable.
2098 */
2099static int
2100unsetvar(const char *s)
2101{
2102 struct var **vpp;
2103 struct var *vp;
2104 int retval;
2105
2106 vpp = findvar(hashvar(s), s);
2107 vp = *vpp;
2108 retval = 2;
2109 if (vp) {
2110 int flags = vp->flags;
2111
2112 retval = 1;
2113 if (flags & VREADONLY)
2114 goto out;
2115#ifdef DYNAMIC_VAR
2116 vp->flags &= ~VDYNAMIC;
2117#endif
2118 if (flags & VUNSET)
2119 goto ok;
2120 if ((flags & VSTRFIXED) == 0) {
2121 INT_OFF;
2122 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2123 free((char*)vp->text);
2124 *vpp = vp->next;
2125 free(vp);
2126 INT_ON;
2127 } else {
2128 setvar(s, 0, 0);
2129 vp->flags &= ~VEXPORT;
2130 }
2131 ok:
2132 retval = 0;
2133 }
2134 out:
2135 return retval;
2136}
2137
2138/*
2139 * Process a linked list of variable assignments.
2140 */
2141static void
2142listsetvar(struct strlist *list_set_var, int flags)
2143{
2144 struct strlist *lp = list_set_var;
2145
2146 if (!lp)
2147 return;
2148 INT_OFF;
2149 do {
2150 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002151 lp = lp->next;
2152 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002153 INT_ON;
2154}
2155
2156/*
2157 * Generate a list of variables satisfying the given conditions.
2158 */
2159static char **
2160listvars(int on, int off, char ***end)
2161{
2162 struct var **vpp;
2163 struct var *vp;
2164 char **ep;
2165 int mask;
2166
2167 STARTSTACKSTR(ep);
2168 vpp = vartab;
2169 mask = on | off;
2170 do {
2171 for (vp = *vpp; vp; vp = vp->next) {
2172 if ((vp->flags & mask) == on) {
2173 if (ep == stackstrend())
2174 ep = growstackstr();
2175 *ep++ = (char *) vp->text;
2176 }
2177 }
2178 } while (++vpp < vartab + VTABSIZE);
2179 if (ep == stackstrend())
2180 ep = growstackstr();
2181 if (end)
2182 *end = ep;
2183 *ep++ = NULL;
2184 return grabstackstr(ep);
2185}
2186
2187
2188/* ============ Path search helper
2189 *
2190 * The variable path (passed by reference) should be set to the start
2191 * of the path before the first call; padvance will update
2192 * this value as it proceeds. Successive calls to padvance will return
2193 * the possible path expansions in sequence. If an option (indicated by
2194 * a percent sign) appears in the path entry then the global variable
2195 * pathopt will be set to point to it; otherwise pathopt will be set to
2196 * NULL.
2197 */
2198static const char *pathopt; /* set by padvance */
2199
2200static char *
2201padvance(const char **path, const char *name)
2202{
2203 const char *p;
2204 char *q;
2205 const char *start;
2206 size_t len;
2207
2208 if (*path == NULL)
2209 return NULL;
2210 start = *path;
2211 for (p = start; *p && *p != ':' && *p != '%'; p++);
2212 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2213 while (stackblocksize() < len)
2214 growstackblock();
2215 q = stackblock();
2216 if (p != start) {
2217 memcpy(q, start, p - start);
2218 q += p - start;
2219 *q++ = '/';
2220 }
2221 strcpy(q, name);
2222 pathopt = NULL;
2223 if (*p == '%') {
2224 pathopt = ++p;
2225 while (*p && *p != ':') p++;
2226 }
2227 if (*p == ':')
2228 *path = p + 1;
2229 else
2230 *path = NULL;
2231 return stalloc(len);
2232}
2233
2234
2235/* ============ Prompt */
2236
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002237static smallint doprompt; /* if set, prompt the user */
2238static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002239
2240#if ENABLE_FEATURE_EDITING
2241static line_input_t *line_input_state;
2242static const char *cmdedit_prompt;
2243static void
2244putprompt(const char *s)
2245{
2246 if (ENABLE_ASH_EXPAND_PRMT) {
2247 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002248 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002249 return;
2250 }
2251 cmdedit_prompt = s;
2252}
2253#else
2254static void
2255putprompt(const char *s)
2256{
2257 out2str(s);
2258}
2259#endif
2260
2261#if ENABLE_ASH_EXPAND_PRMT
2262/* expandstr() needs parsing machinery, so it is far away ahead... */
2263static const char *expandstr(const char *ps);
2264#else
2265#define expandstr(s) s
2266#endif
2267
2268static void
2269setprompt(int whichprompt)
2270{
2271 const char *prompt;
2272#if ENABLE_ASH_EXPAND_PRMT
2273 struct stackmark smark;
2274#endif
2275
2276 needprompt = 0;
2277
2278 switch (whichprompt) {
2279 case 1:
2280 prompt = ps1val();
2281 break;
2282 case 2:
2283 prompt = ps2val();
2284 break;
2285 default: /* 0 */
2286 prompt = nullstr;
2287 }
2288#if ENABLE_ASH_EXPAND_PRMT
2289 setstackmark(&smark);
2290 stalloc(stackblocksize());
2291#endif
2292 putprompt(expandstr(prompt));
2293#if ENABLE_ASH_EXPAND_PRMT
2294 popstackmark(&smark);
2295#endif
2296}
2297
2298
2299/* ============ The cd and pwd commands */
2300
2301#define CD_PHYSICAL 1
2302#define CD_PRINT 2
2303
2304static int docd(const char *, int);
2305
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002306static int
2307cdopt(void)
2308{
2309 int flags = 0;
2310 int i, j;
2311
2312 j = 'L';
2313 while ((i = nextopt("LP"))) {
2314 if (i != j) {
2315 flags ^= CD_PHYSICAL;
2316 j = i;
2317 }
2318 }
2319
2320 return flags;
2321}
2322
2323/*
2324 * Update curdir (the name of the current directory) in response to a
2325 * cd command.
2326 */
2327static const char *
2328updatepwd(const char *dir)
2329{
2330 char *new;
2331 char *p;
2332 char *cdcomppath;
2333 const char *lim;
2334
2335 cdcomppath = ststrdup(dir);
2336 STARTSTACKSTR(new);
2337 if (*dir != '/') {
2338 if (curdir == nullstr)
2339 return 0;
2340 new = stack_putstr(curdir, new);
2341 }
2342 new = makestrspace(strlen(dir) + 2, new);
2343 lim = stackblock() + 1;
2344 if (*dir != '/') {
2345 if (new[-1] != '/')
2346 USTPUTC('/', new);
2347 if (new > lim && *lim == '/')
2348 lim++;
2349 } else {
2350 USTPUTC('/', new);
2351 cdcomppath++;
2352 if (dir[1] == '/' && dir[2] != '/') {
2353 USTPUTC('/', new);
2354 cdcomppath++;
2355 lim++;
2356 }
2357 }
2358 p = strtok(cdcomppath, "/");
2359 while (p) {
2360 switch (*p) {
2361 case '.':
2362 if (p[1] == '.' && p[2] == '\0') {
2363 while (new > lim) {
2364 STUNPUTC(new);
2365 if (new[-1] == '/')
2366 break;
2367 }
2368 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002369 }
2370 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002371 break;
2372 /* fall through */
2373 default:
2374 new = stack_putstr(p, new);
2375 USTPUTC('/', new);
2376 }
2377 p = strtok(0, "/");
2378 }
2379 if (new > lim)
2380 STUNPUTC(new);
2381 *new = 0;
2382 return stackblock();
2383}
2384
2385/*
2386 * Find out what the current directory is. If we already know the current
2387 * directory, this routine returns immediately.
2388 */
2389static char *
2390getpwd(void)
2391{
Denis Vlasenko01631112007-12-16 17:20:38 +00002392 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002393 return dir ? dir : nullstr;
2394}
2395
2396static void
2397setpwd(const char *val, int setold)
2398{
2399 char *oldcur, *dir;
2400
2401 oldcur = dir = curdir;
2402
2403 if (setold) {
2404 setvar("OLDPWD", oldcur, VEXPORT);
2405 }
2406 INT_OFF;
2407 if (physdir != nullstr) {
2408 if (physdir != oldcur)
2409 free(physdir);
2410 physdir = nullstr;
2411 }
2412 if (oldcur == val || !val) {
2413 char *s = getpwd();
2414 physdir = s;
2415 if (!val)
2416 dir = s;
2417 } else
2418 dir = ckstrdup(val);
2419 if (oldcur != dir && oldcur != nullstr) {
2420 free(oldcur);
2421 }
2422 curdir = dir;
2423 INT_ON;
2424 setvar("PWD", dir, VEXPORT);
2425}
2426
2427static void hashcd(void);
2428
2429/*
2430 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2431 * know that the current directory has changed.
2432 */
2433static int
2434docd(const char *dest, int flags)
2435{
2436 const char *dir = 0;
2437 int err;
2438
2439 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2440
2441 INT_OFF;
2442 if (!(flags & CD_PHYSICAL)) {
2443 dir = updatepwd(dest);
2444 if (dir)
2445 dest = dir;
2446 }
2447 err = chdir(dest);
2448 if (err)
2449 goto out;
2450 setpwd(dir, 1);
2451 hashcd();
2452 out:
2453 INT_ON;
2454 return err;
2455}
2456
2457static int
2458cdcmd(int argc, char **argv)
2459{
2460 const char *dest;
2461 const char *path;
2462 const char *p;
2463 char c;
2464 struct stat statb;
2465 int flags;
2466
2467 flags = cdopt();
2468 dest = *argptr;
2469 if (!dest)
2470 dest = bltinlookup(homestr);
2471 else if (LONE_DASH(dest)) {
2472 dest = bltinlookup("OLDPWD");
2473 flags |= CD_PRINT;
2474 }
2475 if (!dest)
2476 dest = nullstr;
2477 if (*dest == '/')
2478 goto step7;
2479 if (*dest == '.') {
2480 c = dest[1];
2481 dotdot:
2482 switch (c) {
2483 case '\0':
2484 case '/':
2485 goto step6;
2486 case '.':
2487 c = dest[2];
2488 if (c != '.')
2489 goto dotdot;
2490 }
2491 }
2492 if (!*dest)
2493 dest = ".";
2494 path = bltinlookup("CDPATH");
2495 if (!path) {
2496 step6:
2497 step7:
2498 p = dest;
2499 goto docd;
2500 }
2501 do {
2502 c = *path;
2503 p = padvance(&path, dest);
2504 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2505 if (c && c != ':')
2506 flags |= CD_PRINT;
2507 docd:
2508 if (!docd(p, flags))
2509 goto out;
2510 break;
2511 }
2512 } while (path);
2513 ash_msg_and_raise_error("can't cd to %s", dest);
2514 /* NOTREACHED */
2515 out:
2516 if (flags & CD_PRINT)
2517 out1fmt(snlfmt, curdir);
2518 return 0;
2519}
2520
2521static int
2522pwdcmd(int argc, char **argv)
2523{
2524 int flags;
2525 const char *dir = curdir;
2526
2527 flags = cdopt();
2528 if (flags) {
2529 if (physdir == nullstr)
2530 setpwd(dir, 0);
2531 dir = physdir;
2532 }
2533 out1fmt(snlfmt, dir);
2534 return 0;
2535}
2536
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002537
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002538/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002539
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002540#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002541#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002542
Eric Andersenc470f442003-07-28 09:56:35 +00002543/* Syntax classes */
2544#define CWORD 0 /* character is nothing special */
2545#define CNL 1 /* newline character */
2546#define CBACK 2 /* a backslash character */
2547#define CSQUOTE 3 /* single quote */
2548#define CDQUOTE 4 /* double quote */
2549#define CENDQUOTE 5 /* a terminating quote */
2550#define CBQUOTE 6 /* backwards single quote */
2551#define CVAR 7 /* a dollar sign */
2552#define CENDVAR 8 /* a '}' character */
2553#define CLP 9 /* a left paren in arithmetic */
2554#define CRP 10 /* a right paren in arithmetic */
2555#define CENDFILE 11 /* end of file */
2556#define CCTL 12 /* like CWORD, except it must be escaped */
2557#define CSPCL 13 /* these terminate a word */
2558#define CIGN 14 /* character should be ignored */
2559
Denis Vlasenko131ae172007-02-18 13:00:19 +00002560#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002561#define SYNBASE 130
2562#define PEOF -130
2563#define PEOA -129
2564#define PEOA_OR_PEOF PEOA
2565#else
2566#define SYNBASE 129
2567#define PEOF -129
2568#define PEOA_OR_PEOF PEOF
2569#endif
2570
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002571/* number syntax index */
2572#define BASESYNTAX 0 /* not in quotes */
2573#define DQSYNTAX 1 /* in double quotes */
2574#define SQSYNTAX 2 /* in single quotes */
2575#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002576#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002577
Denis Vlasenko131ae172007-02-18 13:00:19 +00002578#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002579#define USE_SIT_FUNCTION
2580#endif
2581
Denis Vlasenko131ae172007-02-18 13:00:19 +00002582#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002583static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002584#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002585 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002586#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002587 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2588 { CNL, CNL, CNL, CNL }, /* 2, \n */
2589 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2590 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2591 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2592 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2593 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2594 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2595 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2596 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2597 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002598#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002599 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2600 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2601 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002602#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002603};
Eric Andersenc470f442003-07-28 09:56:35 +00002604#else
2605static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002606#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002607 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002608#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002609 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2610 { CNL, CNL, CNL }, /* 2, \n */
2611 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2612 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2613 { CVAR, CVAR, CWORD }, /* 5, $ */
2614 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2615 { CSPCL, CWORD, CWORD }, /* 7, ( */
2616 { CSPCL, CWORD, CWORD }, /* 8, ) */
2617 { CBACK, CBACK, CCTL }, /* 9, \ */
2618 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2619 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002620#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002621 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2622 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2623 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002624#endif
2625};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002626#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002627
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002628#ifdef USE_SIT_FUNCTION
2629
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002630static int
2631SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002632{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002633 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002634#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002635 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002636 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2637 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2638 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2639 11, 3 /* "}~" */
2640 };
2641#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002642 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002643 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2644 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2645 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2646 10, 2 /* "}~" */
2647 };
2648#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002649 const char *s;
2650 int indx;
2651
Eric Andersenc470f442003-07-28 09:56:35 +00002652 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002653 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002654#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002655 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002657 else
2658#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002659#define U_C(c) ((unsigned char)(c))
2660
2661 if ((unsigned char)c >= (unsigned char)(CTLESC)
2662 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2663 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002664 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002665 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002666 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002667 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002668 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002669 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002670 }
2671 return S_I_T[indx][syntax];
2672}
2673
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002674#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675
Denis Vlasenko131ae172007-02-18 13:00:19 +00002676#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002677#define CSPCL_CIGN_CIGN_CIGN 0
2678#define CSPCL_CWORD_CWORD_CWORD 1
2679#define CNL_CNL_CNL_CNL 2
2680#define CWORD_CCTL_CCTL_CWORD 3
2681#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2682#define CVAR_CVAR_CWORD_CVAR 5
2683#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2684#define CSPCL_CWORD_CWORD_CLP 7
2685#define CSPCL_CWORD_CWORD_CRP 8
2686#define CBACK_CBACK_CCTL_CBACK 9
2687#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2688#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2689#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2690#define CWORD_CWORD_CWORD_CWORD 13
2691#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002692#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002693#define CSPCL_CWORD_CWORD_CWORD 0
2694#define CNL_CNL_CNL_CNL 1
2695#define CWORD_CCTL_CCTL_CWORD 2
2696#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2697#define CVAR_CVAR_CWORD_CVAR 4
2698#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2699#define CSPCL_CWORD_CWORD_CLP 6
2700#define CSPCL_CWORD_CWORD_CRP 7
2701#define CBACK_CBACK_CCTL_CBACK 8
2702#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2703#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2704#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2705#define CWORD_CWORD_CWORD_CWORD 12
2706#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002707#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002708
2709static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002710 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002711 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002712#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002713 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2714#endif
2715 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2716 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2717 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2718 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2719 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2720 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2721 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2722 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2723 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002724 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2725 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2726 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2727 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2728 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2729 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2730 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2731 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2853 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2854 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2876 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002877 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002878 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2879 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2880 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2881 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002882 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002883 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2884 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2885 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2886 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2888 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2889 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2890 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2891 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2903 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2904 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2905 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2906 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2907 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2935 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2936 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2937 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2940 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2968 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2969 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2970 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002971};
2972
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002973#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2974
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002975#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002976
Eric Andersen2870d962001-07-02 17:27:21 +00002977
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002978/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002979
Denis Vlasenko131ae172007-02-18 13:00:19 +00002980#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002981
2982#define ALIASINUSE 1
2983#define ALIASDEAD 2
2984
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002985struct alias {
2986 struct alias *next;
2987 char *name;
2988 char *val;
2989 int flag;
2990};
2991
Denis Vlasenko01631112007-12-16 17:20:38 +00002992
2993static struct alias **atab; // [ATABSIZE];
2994#define INIT_G_alias() do { \
2995 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
2996} while (0)
2997
Eric Andersen2870d962001-07-02 17:27:21 +00002998
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002999static struct alias **
3000__lookupalias(const char *name) {
3001 unsigned int hashval;
3002 struct alias **app;
3003 const char *p;
3004 unsigned int ch;
3005
3006 p = name;
3007
3008 ch = (unsigned char)*p;
3009 hashval = ch << 4;
3010 while (ch) {
3011 hashval += ch;
3012 ch = (unsigned char)*++p;
3013 }
3014 app = &atab[hashval % ATABSIZE];
3015
3016 for (; *app; app = &(*app)->next) {
3017 if (strcmp(name, (*app)->name) == 0) {
3018 break;
3019 }
3020 }
3021
3022 return app;
3023}
3024
3025static struct alias *
3026lookupalias(const char *name, int check)
3027{
3028 struct alias *ap = *__lookupalias(name);
3029
3030 if (check && ap && (ap->flag & ALIASINUSE))
3031 return NULL;
3032 return ap;
3033}
3034
3035static struct alias *
3036freealias(struct alias *ap)
3037{
3038 struct alias *next;
3039
3040 if (ap->flag & ALIASINUSE) {
3041 ap->flag |= ALIASDEAD;
3042 return ap;
3043 }
3044
3045 next = ap->next;
3046 free(ap->name);
3047 free(ap->val);
3048 free(ap);
3049 return next;
3050}
Eric Andersencb57d552001-06-28 07:25:16 +00003051
Eric Andersenc470f442003-07-28 09:56:35 +00003052static void
3053setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003054{
3055 struct alias *ap, **app;
3056
3057 app = __lookupalias(name);
3058 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003059 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003060 if (ap) {
3061 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003062 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003063 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003064 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003065 ap->flag &= ~ALIASDEAD;
3066 } else {
3067 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003068 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003069 ap->name = ckstrdup(name);
3070 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003071 ap->flag = 0;
3072 ap->next = 0;
3073 *app = ap;
3074 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003075 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003076}
3077
Eric Andersenc470f442003-07-28 09:56:35 +00003078static int
3079unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003080{
Eric Andersencb57d552001-06-28 07:25:16 +00003081 struct alias **app;
3082
3083 app = __lookupalias(name);
3084
3085 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003086 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003087 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003088 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003089 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003090 }
3091
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003092 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003093}
3094
Eric Andersenc470f442003-07-28 09:56:35 +00003095static void
3096rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003097{
Eric Andersencb57d552001-06-28 07:25:16 +00003098 struct alias *ap, **app;
3099 int i;
3100
Denis Vlasenkob012b102007-02-19 22:43:01 +00003101 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003102 for (i = 0; i < ATABSIZE; i++) {
3103 app = &atab[i];
3104 for (ap = *app; ap; ap = *app) {
3105 *app = freealias(*app);
3106 if (ap == *app) {
3107 app = &ap->next;
3108 }
3109 }
3110 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003111 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003112}
3113
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003114static void
3115printalias(const struct alias *ap)
3116{
3117 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3118}
3119
Eric Andersencb57d552001-06-28 07:25:16 +00003120/*
3121 * TODO - sort output
3122 */
Eric Andersenc470f442003-07-28 09:56:35 +00003123static int
3124aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003125{
3126 char *n, *v;
3127 int ret = 0;
3128 struct alias *ap;
3129
3130 if (argc == 1) {
3131 int i;
3132
3133 for (i = 0; i < ATABSIZE; i++)
3134 for (ap = atab[i]; ap; ap = ap->next) {
3135 printalias(ap);
3136 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003137 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003138 }
3139 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003140 v = strchr(n+1, '=');
3141 if (v == NULL) { /* n+1: funny ksh stuff */
3142 ap = *__lookupalias(n);
3143 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003144 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003145 ret = 1;
3146 } else
3147 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003148 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003149 *v++ = '\0';
3150 setalias(n, v);
3151 }
3152 }
3153
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003154 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003155}
3156
Eric Andersenc470f442003-07-28 09:56:35 +00003157static int
3158unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003159{
3160 int i;
3161
3162 while ((i = nextopt("a")) != '\0') {
3163 if (i == 'a') {
3164 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003165 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003166 }
3167 }
3168 for (i = 0; *argptr; argptr++) {
3169 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003170 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003171 i = 1;
3172 }
3173 }
3174
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003175 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003176}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003177
Denis Vlasenko131ae172007-02-18 13:00:19 +00003178#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003179
Eric Andersenc470f442003-07-28 09:56:35 +00003180
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003181/* ============ jobs.c */
3182
3183/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3184#define FORK_FG 0
3185#define FORK_BG 1
3186#define FORK_NOJOB 2
3187
3188/* mode flags for showjob(s) */
3189#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3190#define SHOW_PID 0x04 /* include process pid */
3191#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3192
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003193/*
3194 * A job structure contains information about a job. A job is either a
3195 * single process or a set of processes contained in a pipeline. In the
3196 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3197 * array of pids.
3198 */
3199
3200struct procstat {
3201 pid_t pid; /* process id */
3202 int status; /* last process status from wait() */
3203 char *cmd; /* text of command being run */
3204};
3205
3206struct job {
3207 struct procstat ps0; /* status of process */
3208 struct procstat *ps; /* status or processes when more than one */
3209#if JOBS
3210 int stopstatus; /* status of a stopped job */
3211#endif
3212 uint32_t
3213 nprocs: 16, /* number of processes */
3214 state: 8,
3215#define JOBRUNNING 0 /* at least one proc running */
3216#define JOBSTOPPED 1 /* all procs are stopped */
3217#define JOBDONE 2 /* all procs are completed */
3218#if JOBS
3219 sigint: 1, /* job was killed by SIGINT */
3220 jobctl: 1, /* job running under job control */
3221#endif
3222 waited: 1, /* true if this entry has been waited for */
3223 used: 1, /* true if this entry is in used */
3224 changed: 1; /* true if status has changed */
3225 struct job *prev_job; /* previous job */
3226};
3227
3228static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003229static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003230
3231static struct job *makejob(union node *, int);
3232static int forkshell(struct job *, union node *, int);
3233static int waitforjob(struct job *);
3234
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003235#if !JOBS
3236enum { jobctl = 0 };
3237#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003238#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003239static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003240static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003241#endif
3242
3243/*
3244 * Set the signal handler for the specified signal. The routine figures
3245 * out what it should be set to.
3246 */
3247static void
3248setsignal(int signo)
3249{
3250 int action;
3251 char *t, tsig;
3252 struct sigaction act;
3253
3254 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003255 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003256 if (t == NULL)
3257 action = S_DFL;
3258 else if (*t != '\0')
3259 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003260 if (rootshell && action == S_DFL) {
3261 switch (signo) {
3262 case SIGINT:
3263 if (iflag || minusc || sflag == 0)
3264 action = S_CATCH;
3265 break;
3266 case SIGQUIT:
3267#if DEBUG
3268 if (debug)
3269 break;
3270#endif
3271 /* FALLTHROUGH */
3272 case SIGTERM:
3273 if (iflag)
3274 action = S_IGN;
3275 break;
3276#if JOBS
3277 case SIGTSTP:
3278 case SIGTTOU:
3279 if (mflag)
3280 action = S_IGN;
3281 break;
3282#endif
3283 }
3284 }
3285
3286 t = &sigmode[signo - 1];
3287 tsig = *t;
3288 if (tsig == 0) {
3289 /*
3290 * current setting unknown
3291 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003292 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003293 /*
3294 * Pretend it worked; maybe we should give a warning
3295 * here, but other shells don't. We don't alter
3296 * sigmode, so that we retry every time.
3297 */
3298 return;
3299 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003300 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003301 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003302 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003303 if (mflag
3304 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3305 ) {
3306 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003307 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003308 }
3309 }
3310 if (tsig == S_HARD_IGN || tsig == action)
3311 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003312 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003313 switch (action) {
3314 case S_CATCH:
3315 act.sa_handler = onsig;
3316 break;
3317 case S_IGN:
3318 act.sa_handler = SIG_IGN;
3319 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320 }
3321 *t = action;
3322 act.sa_flags = 0;
3323 sigfillset(&act.sa_mask);
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003324 sigaction(signo, &act, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003325}
3326
3327/* mode flags for set_curjob */
3328#define CUR_DELETE 2
3329#define CUR_RUNNING 1
3330#define CUR_STOPPED 0
3331
3332/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003333#define DOWAIT_NONBLOCK WNOHANG
3334#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003335
3336#if JOBS
3337/* pgrp of shell on invocation */
3338static int initialpgrp;
3339static int ttyfd = -1;
3340#endif
3341/* array of jobs */
3342static struct job *jobtab;
3343/* size of array */
3344static unsigned njobs;
3345/* current job */
3346static struct job *curjob;
3347/* number of presumed living untracked jobs */
3348static int jobless;
3349
3350static void
3351set_curjob(struct job *jp, unsigned mode)
3352{
3353 struct job *jp1;
3354 struct job **jpp, **curp;
3355
3356 /* first remove from list */
3357 jpp = curp = &curjob;
3358 do {
3359 jp1 = *jpp;
3360 if (jp1 == jp)
3361 break;
3362 jpp = &jp1->prev_job;
3363 } while (1);
3364 *jpp = jp1->prev_job;
3365
3366 /* Then re-insert in correct position */
3367 jpp = curp;
3368 switch (mode) {
3369 default:
3370#if DEBUG
3371 abort();
3372#endif
3373 case CUR_DELETE:
3374 /* job being deleted */
3375 break;
3376 case CUR_RUNNING:
3377 /* newly created job or backgrounded job,
3378 put after all stopped jobs. */
3379 do {
3380 jp1 = *jpp;
3381#if JOBS
3382 if (!jp1 || jp1->state != JOBSTOPPED)
3383#endif
3384 break;
3385 jpp = &jp1->prev_job;
3386 } while (1);
3387 /* FALLTHROUGH */
3388#if JOBS
3389 case CUR_STOPPED:
3390#endif
3391 /* newly stopped job - becomes curjob */
3392 jp->prev_job = *jpp;
3393 *jpp = jp;
3394 break;
3395 }
3396}
3397
3398#if JOBS || DEBUG
3399static int
3400jobno(const struct job *jp)
3401{
3402 return jp - jobtab + 1;
3403}
3404#endif
3405
3406/*
3407 * Convert a job name to a job structure.
3408 */
3409static struct job *
3410getjob(const char *name, int getctl)
3411{
3412 struct job *jp;
3413 struct job *found;
3414 const char *err_msg = "No such job: %s";
3415 unsigned num;
3416 int c;
3417 const char *p;
3418 char *(*match)(const char *, const char *);
3419
3420 jp = curjob;
3421 p = name;
3422 if (!p)
3423 goto currentjob;
3424
3425 if (*p != '%')
3426 goto err;
3427
3428 c = *++p;
3429 if (!c)
3430 goto currentjob;
3431
3432 if (!p[1]) {
3433 if (c == '+' || c == '%') {
3434 currentjob:
3435 err_msg = "No current job";
3436 goto check;
3437 }
3438 if (c == '-') {
3439 if (jp)
3440 jp = jp->prev_job;
3441 err_msg = "No previous job";
3442 check:
3443 if (!jp)
3444 goto err;
3445 goto gotit;
3446 }
3447 }
3448
3449 if (is_number(p)) {
3450 num = atoi(p);
3451 if (num < njobs) {
3452 jp = jobtab + num - 1;
3453 if (jp->used)
3454 goto gotit;
3455 goto err;
3456 }
3457 }
3458
3459 match = prefix;
3460 if (*p == '?') {
3461 match = strstr;
3462 p++;
3463 }
3464
3465 found = 0;
3466 while (1) {
3467 if (!jp)
3468 goto err;
3469 if (match(jp->ps[0].cmd, p)) {
3470 if (found)
3471 goto err;
3472 found = jp;
3473 err_msg = "%s: ambiguous";
3474 }
3475 jp = jp->prev_job;
3476 }
3477
3478 gotit:
3479#if JOBS
3480 err_msg = "job %s not created under job control";
3481 if (getctl && jp->jobctl == 0)
3482 goto err;
3483#endif
3484 return jp;
3485 err:
3486 ash_msg_and_raise_error(err_msg, name);
3487}
3488
3489/*
3490 * Mark a job structure as unused.
3491 */
3492static void
3493freejob(struct job *jp)
3494{
3495 struct procstat *ps;
3496 int i;
3497
3498 INT_OFF;
3499 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3500 if (ps->cmd != nullstr)
3501 free(ps->cmd);
3502 }
3503 if (jp->ps != &jp->ps0)
3504 free(jp->ps);
3505 jp->used = 0;
3506 set_curjob(jp, CUR_DELETE);
3507 INT_ON;
3508}
3509
3510#if JOBS
3511static void
3512xtcsetpgrp(int fd, pid_t pgrp)
3513{
3514 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003515 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003516}
3517
3518/*
3519 * Turn job control on and off.
3520 *
3521 * Note: This code assumes that the third arg to ioctl is a character
3522 * pointer, which is true on Berkeley systems but not System V. Since
3523 * System V doesn't have job control yet, this isn't a problem now.
3524 *
3525 * Called with interrupts off.
3526 */
3527static void
3528setjobctl(int on)
3529{
3530 int fd;
3531 int pgrp;
3532
3533 if (on == jobctl || rootshell == 0)
3534 return;
3535 if (on) {
3536 int ofd;
3537 ofd = fd = open(_PATH_TTY, O_RDWR);
3538 if (fd < 0) {
3539 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3540 * That sometimes helps to acquire controlling tty.
3541 * Obviously, a workaround for bugs when someone
3542 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003543 fd = 2;
3544 while (!isatty(fd))
3545 if (--fd < 0)
3546 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003547 }
3548 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003549 if (ofd >= 0)
3550 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003551 if (fd < 0)
3552 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003553 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003554 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003555 do { /* while we are in the background */
3556 pgrp = tcgetpgrp(fd);
3557 if (pgrp < 0) {
3558 out:
3559 ash_msg("can't access tty; job control turned off");
3560 mflag = on = 0;
3561 goto close;
3562 }
3563 if (pgrp == getpgrp())
3564 break;
3565 killpg(0, SIGTTIN);
3566 } while (1);
3567 initialpgrp = pgrp;
3568
3569 setsignal(SIGTSTP);
3570 setsignal(SIGTTOU);
3571 setsignal(SIGTTIN);
3572 pgrp = rootpid;
3573 setpgid(0, pgrp);
3574 xtcsetpgrp(fd, pgrp);
3575 } else {
3576 /* turning job control off */
3577 fd = ttyfd;
3578 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003579 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003580 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003581 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003582 setpgid(0, pgrp);
3583 setsignal(SIGTSTP);
3584 setsignal(SIGTTOU);
3585 setsignal(SIGTTIN);
3586 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003587 if (fd >= 0)
3588 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589 fd = -1;
3590 }
3591 ttyfd = fd;
3592 jobctl = on;
3593}
3594
3595static int
3596killcmd(int argc, char **argv)
3597{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003598 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3599 int i = 1;
3600 do {
3601 if (argv[i][0] == '%') {
3602 struct job *jp = getjob(argv[i], 0);
3603 unsigned pid = jp->ps[0].pid;
3604 /* Enough space for ' -NNN<nul>' */
3605 argv[i] = alloca(sizeof(int)*3 + 3);
3606 /* kill_main has matching code to expect
3607 * leading space. Needed to not confuse
3608 * negative pids with "kill -SIGNAL_NO" syntax */
3609 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003610 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003611 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003612 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003613 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003614}
3615
3616static void
3617showpipe(struct job *jp, FILE *out)
3618{
3619 struct procstat *sp;
3620 struct procstat *spend;
3621
3622 spend = jp->ps + jp->nprocs;
3623 for (sp = jp->ps + 1; sp < spend; sp++)
3624 fprintf(out, " | %s", sp->cmd);
3625 outcslow('\n', out);
3626 flush_stdout_stderr();
3627}
3628
3629
3630static int
3631restartjob(struct job *jp, int mode)
3632{
3633 struct procstat *ps;
3634 int i;
3635 int status;
3636 pid_t pgid;
3637
3638 INT_OFF;
3639 if (jp->state == JOBDONE)
3640 goto out;
3641 jp->state = JOBRUNNING;
3642 pgid = jp->ps->pid;
3643 if (mode == FORK_FG)
3644 xtcsetpgrp(ttyfd, pgid);
3645 killpg(pgid, SIGCONT);
3646 ps = jp->ps;
3647 i = jp->nprocs;
3648 do {
3649 if (WIFSTOPPED(ps->status)) {
3650 ps->status = -1;
3651 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003652 ps++;
3653 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654 out:
3655 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3656 INT_ON;
3657 return status;
3658}
3659
3660static int
3661fg_bgcmd(int argc, char **argv)
3662{
3663 struct job *jp;
3664 FILE *out;
3665 int mode;
3666 int retval;
3667
3668 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3669 nextopt(nullstr);
3670 argv = argptr;
3671 out = stdout;
3672 do {
3673 jp = getjob(*argv, 1);
3674 if (mode == FORK_BG) {
3675 set_curjob(jp, CUR_RUNNING);
3676 fprintf(out, "[%d] ", jobno(jp));
3677 }
3678 outstr(jp->ps->cmd, out);
3679 showpipe(jp, out);
3680 retval = restartjob(jp, mode);
3681 } while (*argv && *++argv);
3682 return retval;
3683}
3684#endif
3685
3686static int
3687sprint_status(char *s, int status, int sigonly)
3688{
3689 int col;
3690 int st;
3691
3692 col = 0;
3693 if (!WIFEXITED(status)) {
3694#if JOBS
3695 if (WIFSTOPPED(status))
3696 st = WSTOPSIG(status);
3697 else
3698#endif
3699 st = WTERMSIG(status);
3700 if (sigonly) {
3701 if (st == SIGINT || st == SIGPIPE)
3702 goto out;
3703#if JOBS
3704 if (WIFSTOPPED(status))
3705 goto out;
3706#endif
3707 }
3708 st &= 0x7f;
3709 col = fmtstr(s, 32, strsignal(st));
3710 if (WCOREDUMP(status)) {
3711 col += fmtstr(s + col, 16, " (core dumped)");
3712 }
3713 } else if (!sigonly) {
3714 st = WEXITSTATUS(status);
3715 if (st)
3716 col = fmtstr(s, 16, "Done(%d)", st);
3717 else
3718 col = fmtstr(s, 16, "Done");
3719 }
3720 out:
3721 return col;
3722}
3723
3724/*
3725 * Do a wait system call. If job control is compiled in, we accept
3726 * stopped processes. If block is zero, we return a value of zero
3727 * rather than blocking.
3728 *
3729 * System V doesn't have a non-blocking wait system call. It does
3730 * have a SIGCLD signal that is sent to a process when one of it's
3731 * children dies. The obvious way to use SIGCLD would be to install
3732 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3733 * was received, and have waitproc bump another counter when it got
3734 * the status of a process. Waitproc would then know that a wait
3735 * system call would not block if the two counters were different.
3736 * This approach doesn't work because if a process has children that
3737 * have not been waited for, System V will send it a SIGCLD when it
3738 * installs a signal handler for SIGCLD. What this means is that when
3739 * a child exits, the shell will be sent SIGCLD signals continuously
3740 * until is runs out of stack space, unless it does a wait call before
3741 * restoring the signal handler. The code below takes advantage of
3742 * this (mis)feature by installing a signal handler for SIGCLD and
3743 * then checking to see whether it was called. If there are any
3744 * children to be waited for, it will be.
3745 *
3746 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3747 * waits at all. In this case, the user will not be informed when
3748 * a background process until the next time she runs a real program
3749 * (as opposed to running a builtin command or just typing return),
3750 * and the jobs command may give out of date information.
3751 */
3752static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003753waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003754{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755#if JOBS
3756 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003757 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003758#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003759 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3760 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003761}
3762
3763/*
3764 * Wait for a process to terminate.
3765 */
3766static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003767dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003768{
3769 int pid;
3770 int status;
3771 struct job *jp;
3772 struct job *thisjob;
3773 int state;
3774
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003775 TRACE(("dowait(%d) called\n", wait_flags));
3776 pid = waitproc(wait_flags, &status);
3777 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003778 if (pid <= 0) {
3779 /* If we were doing blocking wait and (probably) got EINTR,
3780 * check for pending sigs received while waiting.
3781 * (NB: can be moved into callers if needed) */
3782 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3783 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003785 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786 INT_OFF;
3787 thisjob = NULL;
3788 for (jp = curjob; jp; jp = jp->prev_job) {
3789 struct procstat *sp;
3790 struct procstat *spend;
3791 if (jp->state == JOBDONE)
3792 continue;
3793 state = JOBDONE;
3794 spend = jp->ps + jp->nprocs;
3795 sp = jp->ps;
3796 do {
3797 if (sp->pid == pid) {
3798 TRACE(("Job %d: changing status of proc %d "
3799 "from 0x%x to 0x%x\n",
3800 jobno(jp), pid, sp->status, status));
3801 sp->status = status;
3802 thisjob = jp;
3803 }
3804 if (sp->status == -1)
3805 state = JOBRUNNING;
3806#if JOBS
3807 if (state == JOBRUNNING)
3808 continue;
3809 if (WIFSTOPPED(sp->status)) {
3810 jp->stopstatus = sp->status;
3811 state = JOBSTOPPED;
3812 }
3813#endif
3814 } while (++sp < spend);
3815 if (thisjob)
3816 goto gotjob;
3817 }
3818#if JOBS
3819 if (!WIFSTOPPED(status))
3820#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 jobless--;
3822 goto out;
3823
3824 gotjob:
3825 if (state != JOBRUNNING) {
3826 thisjob->changed = 1;
3827
3828 if (thisjob->state != state) {
3829 TRACE(("Job %d: changing state from %d to %d\n",
3830 jobno(thisjob), thisjob->state, state));
3831 thisjob->state = state;
3832#if JOBS
3833 if (state == JOBSTOPPED) {
3834 set_curjob(thisjob, CUR_STOPPED);
3835 }
3836#endif
3837 }
3838 }
3839
3840 out:
3841 INT_ON;
3842
3843 if (thisjob && thisjob == job) {
3844 char s[48 + 1];
3845 int len;
3846
3847 len = sprint_status(s, status, 1);
3848 if (len) {
3849 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003850 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003851 out2str(s);
3852 }
3853 }
3854 return pid;
3855}
3856
3857#if JOBS
3858static void
3859showjob(FILE *out, struct job *jp, int mode)
3860{
3861 struct procstat *ps;
3862 struct procstat *psend;
3863 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003864 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003865 char s[80];
3866
3867 ps = jp->ps;
3868
3869 if (mode & SHOW_PGID) {
3870 /* just output process (group) id of pipeline */
3871 fprintf(out, "%d\n", ps->pid);
3872 return;
3873 }
3874
3875 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003876 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003877
3878 if (jp == curjob)
3879 s[col - 2] = '+';
3880 else if (curjob && jp == curjob->prev_job)
3881 s[col - 2] = '-';
3882
3883 if (mode & SHOW_PID)
3884 col += fmtstr(s + col, 16, "%d ", ps->pid);
3885
3886 psend = ps + jp->nprocs;
3887
3888 if (jp->state == JOBRUNNING) {
3889 strcpy(s + col, "Running");
3890 col += sizeof("Running") - 1;
3891 } else {
3892 int status = psend[-1].status;
3893 if (jp->state == JOBSTOPPED)
3894 status = jp->stopstatus;
3895 col += sprint_status(s + col, status, 0);
3896 }
3897
3898 goto start;
3899
3900 do {
3901 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003902 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903 start:
3904 fprintf(out, "%s%*c%s",
3905 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3906 );
3907 if (!(mode & SHOW_PID)) {
3908 showpipe(jp, out);
3909 break;
3910 }
3911 if (++ps == psend) {
3912 outcslow('\n', out);
3913 break;
3914 }
3915 } while (1);
3916
3917 jp->changed = 0;
3918
3919 if (jp->state == JOBDONE) {
3920 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3921 freejob(jp);
3922 }
3923}
3924
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925/*
3926 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3927 * statuses have changed since the last call to showjobs.
3928 */
3929static void
3930showjobs(FILE *out, int mode)
3931{
3932 struct job *jp;
3933
3934 TRACE(("showjobs(%x) called\n", mode));
3935
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003936 /* If not even one job changed, there is nothing to do */
3937 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 continue;
3939
3940 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003941 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003943 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944 }
3945}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003946
3947static int
3948jobscmd(int argc, char **argv)
3949{
3950 int mode, m;
3951
3952 mode = 0;
3953 while ((m = nextopt("lp"))) {
3954 if (m == 'l')
3955 mode = SHOW_PID;
3956 else
3957 mode = SHOW_PGID;
3958 }
3959
3960 argv = argptr;
3961 if (*argv) {
3962 do
3963 showjob(stdout, getjob(*argv,0), mode);
3964 while (*++argv);
3965 } else
3966 showjobs(stdout, mode);
3967
3968 return 0;
3969}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970#endif /* JOBS */
3971
3972static int
3973getstatus(struct job *job)
3974{
3975 int status;
3976 int retval;
3977
3978 status = job->ps[job->nprocs - 1].status;
3979 retval = WEXITSTATUS(status);
3980 if (!WIFEXITED(status)) {
3981#if JOBS
3982 retval = WSTOPSIG(status);
3983 if (!WIFSTOPPED(status))
3984#endif
3985 {
3986 /* XXX: limits number of signals */
3987 retval = WTERMSIG(status);
3988#if JOBS
3989 if (retval == SIGINT)
3990 job->sigint = 1;
3991#endif
3992 }
3993 retval += 128;
3994 }
3995 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
3996 jobno(job), job->nprocs, status, retval));
3997 return retval;
3998}
3999
4000static int
4001waitcmd(int argc, char **argv)
4002{
4003 struct job *job;
4004 int retval;
4005 struct job *jp;
4006
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004007// exsig++;
4008// xbarrier();
4009 if (pendingsig)
4010 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004011
4012 nextopt(nullstr);
4013 retval = 0;
4014
4015 argv = argptr;
4016 if (!*argv) {
4017 /* wait for all jobs */
4018 for (;;) {
4019 jp = curjob;
4020 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004021 if (!jp) /* no running procs */
4022 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004023 if (jp->state == JOBRUNNING)
4024 break;
4025 jp->waited = 1;
4026 jp = jp->prev_job;
4027 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004028 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004029 }
4030 }
4031
4032 retval = 127;
4033 do {
4034 if (**argv != '%') {
4035 pid_t pid = number(*argv);
4036 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004037 while (1) {
4038 if (!job)
4039 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004040 if (job->ps[job->nprocs - 1].pid == pid)
4041 break;
4042 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004043 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004044 } else
4045 job = getjob(*argv, 0);
4046 /* loop until process terminated or stopped */
4047 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004048 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004049 job->waited = 1;
4050 retval = getstatus(job);
4051 repeat:
4052 ;
4053 } while (*++argv);
4054
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004055 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004056 return retval;
4057}
4058
4059static struct job *
4060growjobtab(void)
4061{
4062 size_t len;
4063 ptrdiff_t offset;
4064 struct job *jp, *jq;
4065
4066 len = njobs * sizeof(*jp);
4067 jq = jobtab;
4068 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4069
4070 offset = (char *)jp - (char *)jq;
4071 if (offset) {
4072 /* Relocate pointers */
4073 size_t l = len;
4074
4075 jq = (struct job *)((char *)jq + l);
4076 while (l) {
4077 l -= sizeof(*jp);
4078 jq--;
4079#define joff(p) ((struct job *)((char *)(p) + l))
4080#define jmove(p) (p) = (void *)((char *)(p) + offset)
4081 if (joff(jp)->ps == &jq->ps0)
4082 jmove(joff(jp)->ps);
4083 if (joff(jp)->prev_job)
4084 jmove(joff(jp)->prev_job);
4085 }
4086 if (curjob)
4087 jmove(curjob);
4088#undef joff
4089#undef jmove
4090 }
4091
4092 njobs += 4;
4093 jobtab = jp;
4094 jp = (struct job *)((char *)jp + len);
4095 jq = jp + 3;
4096 do {
4097 jq->used = 0;
4098 } while (--jq >= jp);
4099 return jp;
4100}
4101
4102/*
4103 * Return a new job structure.
4104 * Called with interrupts off.
4105 */
4106static struct job *
4107makejob(union node *node, int nprocs)
4108{
4109 int i;
4110 struct job *jp;
4111
4112 for (i = njobs, jp = jobtab; ; jp++) {
4113 if (--i < 0) {
4114 jp = growjobtab();
4115 break;
4116 }
4117 if (jp->used == 0)
4118 break;
4119 if (jp->state != JOBDONE || !jp->waited)
4120 continue;
4121#if JOBS
4122 if (jobctl)
4123 continue;
4124#endif
4125 freejob(jp);
4126 break;
4127 }
4128 memset(jp, 0, sizeof(*jp));
4129#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004130 /* jp->jobctl is a bitfield.
4131 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004132 if (jobctl)
4133 jp->jobctl = 1;
4134#endif
4135 jp->prev_job = curjob;
4136 curjob = jp;
4137 jp->used = 1;
4138 jp->ps = &jp->ps0;
4139 if (nprocs > 1) {
4140 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4141 }
4142 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4143 jobno(jp)));
4144 return jp;
4145}
4146
4147#if JOBS
4148/*
4149 * Return a string identifying a command (to be printed by the
4150 * jobs command).
4151 */
4152static char *cmdnextc;
4153
4154static void
4155cmdputs(const char *s)
4156{
4157 const char *p, *str;
4158 char c, cc[2] = " ";
4159 char *nextc;
4160 int subtype = 0;
4161 int quoted = 0;
4162 static const char vstype[VSTYPE + 1][4] = {
4163 "", "}", "-", "+", "?", "=",
4164 "%", "%%", "#", "##"
4165 };
4166
4167 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4168 p = s;
4169 while ((c = *p++) != 0) {
4170 str = 0;
4171 switch (c) {
4172 case CTLESC:
4173 c = *p++;
4174 break;
4175 case CTLVAR:
4176 subtype = *p++;
4177 if ((subtype & VSTYPE) == VSLENGTH)
4178 str = "${#";
4179 else
4180 str = "${";
4181 if (!(subtype & VSQUOTE) == !(quoted & 1))
4182 goto dostr;
4183 quoted ^= 1;
4184 c = '"';
4185 break;
4186 case CTLENDVAR:
4187 str = "\"}" + !(quoted & 1);
4188 quoted >>= 1;
4189 subtype = 0;
4190 goto dostr;
4191 case CTLBACKQ:
4192 str = "$(...)";
4193 goto dostr;
4194 case CTLBACKQ+CTLQUOTE:
4195 str = "\"$(...)\"";
4196 goto dostr;
4197#if ENABLE_ASH_MATH_SUPPORT
4198 case CTLARI:
4199 str = "$((";
4200 goto dostr;
4201 case CTLENDARI:
4202 str = "))";
4203 goto dostr;
4204#endif
4205 case CTLQUOTEMARK:
4206 quoted ^= 1;
4207 c = '"';
4208 break;
4209 case '=':
4210 if (subtype == 0)
4211 break;
4212 if ((subtype & VSTYPE) != VSNORMAL)
4213 quoted <<= 1;
4214 str = vstype[subtype & VSTYPE];
4215 if (subtype & VSNUL)
4216 c = ':';
4217 else
4218 goto checkstr;
4219 break;
4220 case '\'':
4221 case '\\':
4222 case '"':
4223 case '$':
4224 /* These can only happen inside quotes */
4225 cc[0] = c;
4226 str = cc;
4227 c = '\\';
4228 break;
4229 default:
4230 break;
4231 }
4232 USTPUTC(c, nextc);
4233 checkstr:
4234 if (!str)
4235 continue;
4236 dostr:
4237 while ((c = *str++)) {
4238 USTPUTC(c, nextc);
4239 }
4240 }
4241 if (quoted & 1) {
4242 USTPUTC('"', nextc);
4243 }
4244 *nextc = 0;
4245 cmdnextc = nextc;
4246}
4247
4248/* cmdtxt() and cmdlist() call each other */
4249static void cmdtxt(union node *n);
4250
4251static void
4252cmdlist(union node *np, int sep)
4253{
4254 for (; np; np = np->narg.next) {
4255 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004256 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004257 cmdtxt(np);
4258 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004259 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004260 }
4261}
4262
4263static void
4264cmdtxt(union node *n)
4265{
4266 union node *np;
4267 struct nodelist *lp;
4268 const char *p;
4269 char s[2];
4270
4271 if (!n)
4272 return;
4273 switch (n->type) {
4274 default:
4275#if DEBUG
4276 abort();
4277#endif
4278 case NPIPE:
4279 lp = n->npipe.cmdlist;
4280 for (;;) {
4281 cmdtxt(lp->n);
4282 lp = lp->next;
4283 if (!lp)
4284 break;
4285 cmdputs(" | ");
4286 }
4287 break;
4288 case NSEMI:
4289 p = "; ";
4290 goto binop;
4291 case NAND:
4292 p = " && ";
4293 goto binop;
4294 case NOR:
4295 p = " || ";
4296 binop:
4297 cmdtxt(n->nbinary.ch1);
4298 cmdputs(p);
4299 n = n->nbinary.ch2;
4300 goto donode;
4301 case NREDIR:
4302 case NBACKGND:
4303 n = n->nredir.n;
4304 goto donode;
4305 case NNOT:
4306 cmdputs("!");
4307 n = n->nnot.com;
4308 donode:
4309 cmdtxt(n);
4310 break;
4311 case NIF:
4312 cmdputs("if ");
4313 cmdtxt(n->nif.test);
4314 cmdputs("; then ");
4315 n = n->nif.ifpart;
4316 if (n->nif.elsepart) {
4317 cmdtxt(n);
4318 cmdputs("; else ");
4319 n = n->nif.elsepart;
4320 }
4321 p = "; fi";
4322 goto dotail;
4323 case NSUBSHELL:
4324 cmdputs("(");
4325 n = n->nredir.n;
4326 p = ")";
4327 goto dotail;
4328 case NWHILE:
4329 p = "while ";
4330 goto until;
4331 case NUNTIL:
4332 p = "until ";
4333 until:
4334 cmdputs(p);
4335 cmdtxt(n->nbinary.ch1);
4336 n = n->nbinary.ch2;
4337 p = "; done";
4338 dodo:
4339 cmdputs("; do ");
4340 dotail:
4341 cmdtxt(n);
4342 goto dotail2;
4343 case NFOR:
4344 cmdputs("for ");
4345 cmdputs(n->nfor.var);
4346 cmdputs(" in ");
4347 cmdlist(n->nfor.args, 1);
4348 n = n->nfor.body;
4349 p = "; done";
4350 goto dodo;
4351 case NDEFUN:
4352 cmdputs(n->narg.text);
4353 p = "() { ... }";
4354 goto dotail2;
4355 case NCMD:
4356 cmdlist(n->ncmd.args, 1);
4357 cmdlist(n->ncmd.redirect, 0);
4358 break;
4359 case NARG:
4360 p = n->narg.text;
4361 dotail2:
4362 cmdputs(p);
4363 break;
4364 case NHERE:
4365 case NXHERE:
4366 p = "<<...";
4367 goto dotail2;
4368 case NCASE:
4369 cmdputs("case ");
4370 cmdputs(n->ncase.expr->narg.text);
4371 cmdputs(" in ");
4372 for (np = n->ncase.cases; np; np = np->nclist.next) {
4373 cmdtxt(np->nclist.pattern);
4374 cmdputs(") ");
4375 cmdtxt(np->nclist.body);
4376 cmdputs(";; ");
4377 }
4378 p = "esac";
4379 goto dotail2;
4380 case NTO:
4381 p = ">";
4382 goto redir;
4383 case NCLOBBER:
4384 p = ">|";
4385 goto redir;
4386 case NAPPEND:
4387 p = ">>";
4388 goto redir;
4389 case NTOFD:
4390 p = ">&";
4391 goto redir;
4392 case NFROM:
4393 p = "<";
4394 goto redir;
4395 case NFROMFD:
4396 p = "<&";
4397 goto redir;
4398 case NFROMTO:
4399 p = "<>";
4400 redir:
4401 s[0] = n->nfile.fd + '0';
4402 s[1] = '\0';
4403 cmdputs(s);
4404 cmdputs(p);
4405 if (n->type == NTOFD || n->type == NFROMFD) {
4406 s[0] = n->ndup.dupfd + '0';
4407 p = s;
4408 goto dotail2;
4409 }
4410 n = n->nfile.fname;
4411 goto donode;
4412 }
4413}
4414
4415static char *
4416commandtext(union node *n)
4417{
4418 char *name;
4419
4420 STARTSTACKSTR(cmdnextc);
4421 cmdtxt(n);
4422 name = stackblock();
4423 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4424 name, cmdnextc, cmdnextc));
4425 return ckstrdup(name);
4426}
4427#endif /* JOBS */
4428
4429/*
4430 * Fork off a subshell. If we are doing job control, give the subshell its
4431 * own process group. Jp is a job structure that the job is to be added to.
4432 * N is the command that will be evaluated by the child. Both jp and n may
4433 * be NULL. The mode parameter can be one of the following:
4434 * FORK_FG - Fork off a foreground process.
4435 * FORK_BG - Fork off a background process.
4436 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4437 * process group even if job control is on.
4438 *
4439 * When job control is turned off, background processes have their standard
4440 * input redirected to /dev/null (except for the second and later processes
4441 * in a pipeline).
4442 *
4443 * Called with interrupts off.
4444 */
4445/*
4446 * Clear traps on a fork.
4447 */
4448static void
4449clear_traps(void)
4450{
4451 char **tp;
4452
4453 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004454 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004455 INT_OFF;
4456 free(*tp);
4457 *tp = NULL;
4458 if (tp != &trap[0])
4459 setsignal(tp - trap);
4460 INT_ON;
4461 }
4462 }
4463}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004464
4465/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004466static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004467
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004468/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469static void
4470forkchild(struct job *jp, union node *n, int mode)
4471{
4472 int oldlvl;
4473
4474 TRACE(("Child shell %d\n", getpid()));
4475 oldlvl = shlvl;
4476 shlvl++;
4477
4478 closescript();
4479 clear_traps();
4480#if JOBS
4481 /* do job control only in root shell */
4482 jobctl = 0;
4483 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4484 pid_t pgrp;
4485
4486 if (jp->nprocs == 0)
4487 pgrp = getpid();
4488 else
4489 pgrp = jp->ps[0].pid;
4490 /* This can fail because we are doing it in the parent also */
4491 (void)setpgid(0, pgrp);
4492 if (mode == FORK_FG)
4493 xtcsetpgrp(ttyfd, pgrp);
4494 setsignal(SIGTSTP);
4495 setsignal(SIGTTOU);
4496 } else
4497#endif
4498 if (mode == FORK_BG) {
4499 ignoresig(SIGINT);
4500 ignoresig(SIGQUIT);
4501 if (jp->nprocs == 0) {
4502 close(0);
4503 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004504 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505 }
4506 }
4507 if (!oldlvl && iflag) {
4508 setsignal(SIGINT);
4509 setsignal(SIGQUIT);
4510 setsignal(SIGTERM);
4511 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512 for (jp = curjob; jp; jp = jp->prev_job)
4513 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004514 jobless = 0;
4515}
4516
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004517/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004518static void
4519forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4520{
4521 TRACE(("In parent shell: child = %d\n", pid));
4522 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004523 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4524 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004525 jobless++;
4526 return;
4527 }
4528#if JOBS
4529 if (mode != FORK_NOJOB && jp->jobctl) {
4530 int pgrp;
4531
4532 if (jp->nprocs == 0)
4533 pgrp = pid;
4534 else
4535 pgrp = jp->ps[0].pid;
4536 /* This can fail because we are doing it in the child also */
4537 setpgid(pid, pgrp);
4538 }
4539#endif
4540 if (mode == FORK_BG) {
4541 backgndpid = pid; /* set $! */
4542 set_curjob(jp, CUR_RUNNING);
4543 }
4544 if (jp) {
4545 struct procstat *ps = &jp->ps[jp->nprocs++];
4546 ps->pid = pid;
4547 ps->status = -1;
4548 ps->cmd = nullstr;
4549#if JOBS
4550 if (jobctl && n)
4551 ps->cmd = commandtext(n);
4552#endif
4553 }
4554}
4555
4556static int
4557forkshell(struct job *jp, union node *n, int mode)
4558{
4559 int pid;
4560
4561 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4562 pid = fork();
4563 if (pid < 0) {
4564 TRACE(("Fork failed, errno=%d", errno));
4565 if (jp)
4566 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004567 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568 }
4569 if (pid == 0)
4570 forkchild(jp, n, mode);
4571 else
4572 forkparent(jp, n, mode, pid);
4573 return pid;
4574}
4575
4576/*
4577 * Wait for job to finish.
4578 *
4579 * Under job control we have the problem that while a child process is
4580 * running interrupts generated by the user are sent to the child but not
4581 * to the shell. This means that an infinite loop started by an inter-
4582 * active user may be hard to kill. With job control turned off, an
4583 * interactive user may place an interactive program inside a loop. If
4584 * the interactive program catches interrupts, the user doesn't want
4585 * these interrupts to also abort the loop. The approach we take here
4586 * is to have the shell ignore interrupt signals while waiting for a
4587 * foreground process to terminate, and then send itself an interrupt
4588 * signal if the child process was terminated by an interrupt signal.
4589 * Unfortunately, some programs want to do a bit of cleanup and then
4590 * exit on interrupt; unless these processes terminate themselves by
4591 * sending a signal to themselves (instead of calling exit) they will
4592 * confuse this approach.
4593 *
4594 * Called with interrupts off.
4595 */
4596static int
4597waitforjob(struct job *jp)
4598{
4599 int st;
4600
4601 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4602 while (jp->state == JOBRUNNING) {
4603 dowait(DOWAIT_BLOCK, jp);
4604 }
4605 st = getstatus(jp);
4606#if JOBS
4607 if (jp->jobctl) {
4608 xtcsetpgrp(ttyfd, rootpid);
4609 /*
4610 * This is truly gross.
4611 * If we're doing job control, then we did a TIOCSPGRP which
4612 * caused us (the shell) to no longer be in the controlling
4613 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4614 * intuit from the subprocess exit status whether a SIGINT
4615 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4616 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004617 if (jp->sigint) /* TODO: do the same with all signals */
4618 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004619 }
4620 if (jp->state == JOBDONE)
4621#endif
4622 freejob(jp);
4623 return st;
4624}
4625
4626/*
4627 * return 1 if there are stopped jobs, otherwise 0
4628 */
4629static int
4630stoppedjobs(void)
4631{
4632 struct job *jp;
4633 int retval;
4634
4635 retval = 0;
4636 if (job_warning)
4637 goto out;
4638 jp = curjob;
4639 if (jp && jp->state == JOBSTOPPED) {
4640 out2str("You have stopped jobs.\n");
4641 job_warning = 2;
4642 retval++;
4643 }
4644 out:
4645 return retval;
4646}
4647
4648
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004649/* ============ redir.c
4650 *
4651 * Code for dealing with input/output redirection.
4652 */
4653
4654#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004655#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004656#ifndef PIPE_BUF
4657# define PIPESIZE 4096 /* amount of buffering in a pipe */
4658#else
4659# define PIPESIZE PIPE_BUF
4660#endif
4661
4662/*
4663 * Open a file in noclobber mode.
4664 * The code was copied from bash.
4665 */
4666static int
4667noclobberopen(const char *fname)
4668{
4669 int r, fd;
4670 struct stat finfo, finfo2;
4671
4672 /*
4673 * If the file exists and is a regular file, return an error
4674 * immediately.
4675 */
4676 r = stat(fname, &finfo);
4677 if (r == 0 && S_ISREG(finfo.st_mode)) {
4678 errno = EEXIST;
4679 return -1;
4680 }
4681
4682 /*
4683 * If the file was not present (r != 0), make sure we open it
4684 * exclusively so that if it is created before we open it, our open
4685 * will fail. Make sure that we do not truncate an existing file.
4686 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4687 * file was not a regular file, we leave O_EXCL off.
4688 */
4689 if (r != 0)
4690 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4691 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4692
4693 /* If the open failed, return the file descriptor right away. */
4694 if (fd < 0)
4695 return fd;
4696
4697 /*
4698 * OK, the open succeeded, but the file may have been changed from a
4699 * non-regular file to a regular file between the stat and the open.
4700 * We are assuming that the O_EXCL open handles the case where FILENAME
4701 * did not exist and is symlinked to an existing file between the stat
4702 * and open.
4703 */
4704
4705 /*
4706 * If we can open it and fstat the file descriptor, and neither check
4707 * revealed that it was a regular file, and the file has not been
4708 * replaced, return the file descriptor.
4709 */
4710 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4711 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4712 return fd;
4713
4714 /* The file has been replaced. badness. */
4715 close(fd);
4716 errno = EEXIST;
4717 return -1;
4718}
4719
4720/*
4721 * Handle here documents. Normally we fork off a process to write the
4722 * data to a pipe. If the document is short, we can stuff the data in
4723 * the pipe without forking.
4724 */
4725/* openhere needs this forward reference */
4726static void expandhere(union node *arg, int fd);
4727static int
4728openhere(union node *redir)
4729{
4730 int pip[2];
4731 size_t len = 0;
4732
4733 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004734 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004735 if (redir->type == NHERE) {
4736 len = strlen(redir->nhere.doc->narg.text);
4737 if (len <= PIPESIZE) {
4738 full_write(pip[1], redir->nhere.doc->narg.text, len);
4739 goto out;
4740 }
4741 }
4742 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4743 close(pip[0]);
4744 signal(SIGINT, SIG_IGN);
4745 signal(SIGQUIT, SIG_IGN);
4746 signal(SIGHUP, SIG_IGN);
4747#ifdef SIGTSTP
4748 signal(SIGTSTP, SIG_IGN);
4749#endif
4750 signal(SIGPIPE, SIG_DFL);
4751 if (redir->type == NHERE)
4752 full_write(pip[1], redir->nhere.doc->narg.text, len);
4753 else
4754 expandhere(redir->nhere.doc, pip[1]);
4755 _exit(0);
4756 }
4757 out:
4758 close(pip[1]);
4759 return pip[0];
4760}
4761
4762static int
4763openredirect(union node *redir)
4764{
4765 char *fname;
4766 int f;
4767
4768 switch (redir->nfile.type) {
4769 case NFROM:
4770 fname = redir->nfile.expfname;
4771 f = open(fname, O_RDONLY);
4772 if (f < 0)
4773 goto eopen;
4774 break;
4775 case NFROMTO:
4776 fname = redir->nfile.expfname;
4777 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4778 if (f < 0)
4779 goto ecreate;
4780 break;
4781 case NTO:
4782 /* Take care of noclobber mode. */
4783 if (Cflag) {
4784 fname = redir->nfile.expfname;
4785 f = noclobberopen(fname);
4786 if (f < 0)
4787 goto ecreate;
4788 break;
4789 }
4790 /* FALLTHROUGH */
4791 case NCLOBBER:
4792 fname = redir->nfile.expfname;
4793 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4794 if (f < 0)
4795 goto ecreate;
4796 break;
4797 case NAPPEND:
4798 fname = redir->nfile.expfname;
4799 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4800 if (f < 0)
4801 goto ecreate;
4802 break;
4803 default:
4804#if DEBUG
4805 abort();
4806#endif
4807 /* Fall through to eliminate warning. */
4808 case NTOFD:
4809 case NFROMFD:
4810 f = -1;
4811 break;
4812 case NHERE:
4813 case NXHERE:
4814 f = openhere(redir);
4815 break;
4816 }
4817
4818 return f;
4819 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004820 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004821 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004822 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004823}
4824
4825/*
4826 * Copy a file descriptor to be >= to. Returns -1
4827 * if the source file descriptor is closed, EMPTY if there are no unused
4828 * file descriptors left.
4829 */
4830static int
4831copyfd(int from, int to)
4832{
4833 int newfd;
4834
4835 newfd = fcntl(from, F_DUPFD, to);
4836 if (newfd < 0) {
4837 if (errno == EMFILE)
4838 return EMPTY;
4839 ash_msg_and_raise_error("%d: %m", from);
4840 }
4841 return newfd;
4842}
4843
4844static void
4845dupredirect(union node *redir, int f)
4846{
4847 int fd = redir->nfile.fd;
4848
4849 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4850 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4851 copyfd(redir->ndup.dupfd, fd);
4852 }
4853 return;
4854 }
4855
4856 if (f != fd) {
4857 copyfd(f, fd);
4858 close(f);
4859 }
4860}
4861
4862/*
4863 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4864 * old file descriptors are stashed away so that the redirection can be
4865 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4866 * standard output, and the standard error if it becomes a duplicate of
4867 * stdout, is saved in memory.
4868 */
4869/* flags passed to redirect */
4870#define REDIR_PUSH 01 /* save previous values of file descriptors */
4871#define REDIR_SAVEFD2 03 /* set preverrout */
4872static void
4873redirect(union node *redir, int flags)
4874{
4875 union node *n;
4876 struct redirtab *sv;
4877 int i;
4878 int fd;
4879 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004880
Denis Vlasenko01631112007-12-16 17:20:38 +00004881 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004882 if (!redir) {
4883 return;
4884 }
4885 sv = NULL;
4886 INT_OFF;
4887 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004888 sv = ckmalloc(sizeof(*sv));
4889 sv->next = redirlist;
4890 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004891 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004892 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004893 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004894 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004895 }
4896 n = redir;
4897 do {
4898 fd = n->nfile.fd;
4899 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4900 && n->ndup.dupfd == fd)
4901 continue; /* redirect from/to same file descriptor */
4902
4903 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004904 if (fd == newfd) {
4905 /* Descriptor wasn't open before redirect.
4906 * Mark it for close in the future */
4907 if (sv && sv->renamed[fd] == EMPTY)
4908 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004909 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004910 }
4911 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004912 i = fcntl(fd, F_DUPFD, 10);
4913
4914 if (i == -1) {
4915 i = errno;
4916 if (i != EBADF) {
4917 close(newfd);
4918 errno = i;
4919 ash_msg_and_raise_error("%d: %m", fd);
4920 /* NOTREACHED */
4921 }
4922 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004923 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004924 close(fd);
4925 }
4926 } else {
4927 close(fd);
4928 }
4929 dupredirect(n, newfd);
4930 } while ((n = n->nfile.next));
4931 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004932 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004933 preverrout_fd = sv->renamed[2];
4934}
4935
4936/*
4937 * Undo the effects of the last redirection.
4938 */
4939static void
4940popredir(int drop)
4941{
4942 struct redirtab *rp;
4943 int i;
4944
Denis Vlasenko01631112007-12-16 17:20:38 +00004945 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004946 return;
4947 INT_OFF;
4948 rp = redirlist;
4949 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004950 if (rp->renamed[i] == CLOSED) {
4951 if (!drop)
4952 close(i);
4953 continue;
4954 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004955 if (rp->renamed[i] != EMPTY) {
4956 if (!drop) {
4957 close(i);
4958 copyfd(rp->renamed[i], i);
4959 }
4960 close(rp->renamed[i]);
4961 }
4962 }
4963 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004964 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004965 free(rp);
4966 INT_ON;
4967}
4968
4969/*
4970 * Undo all redirections. Called on error or interrupt.
4971 */
4972
4973/*
4974 * Discard all saved file descriptors.
4975 */
4976static void
4977clearredir(int drop)
4978{
4979 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004980 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004981 if (!redirlist)
4982 break;
4983 popredir(drop);
4984 }
4985}
4986
4987static int
4988redirectsafe(union node *redir, int flags)
4989{
4990 int err;
4991 volatile int saveint;
4992 struct jmploc *volatile savehandler = exception_handler;
4993 struct jmploc jmploc;
4994
4995 SAVE_INT(saveint);
4996 err = setjmp(jmploc.loc) * 2;
4997 if (!err) {
4998 exception_handler = &jmploc;
4999 redirect(redir, flags);
5000 }
5001 exception_handler = savehandler;
5002 if (err && exception != EXERROR)
5003 longjmp(exception_handler->loc, 1);
5004 RESTORE_INT(saveint);
5005 return err;
5006}
5007
5008
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005009/* ============ Routines to expand arguments to commands
5010 *
5011 * We have to deal with backquotes, shell variables, and file metacharacters.
5012 */
5013
5014/*
5015 * expandarg flags
5016 */
5017#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5018#define EXP_TILDE 0x2 /* do normal tilde expansion */
5019#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5020#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5021#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5022#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5023#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5024#define EXP_WORD 0x80 /* expand word in parameter expansion */
5025#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5026/*
5027 * _rmescape() flags
5028 */
5029#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5030#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5031#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5032#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5033#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5034
5035/*
5036 * Structure specifying which parts of the string should be searched
5037 * for IFS characters.
5038 */
5039struct ifsregion {
5040 struct ifsregion *next; /* next region in list */
5041 int begoff; /* offset of start of region */
5042 int endoff; /* offset of end of region */
5043 int nulonly; /* search for nul bytes only */
5044};
5045
5046struct arglist {
5047 struct strlist *list;
5048 struct strlist **lastp;
5049};
5050
5051/* output of current string */
5052static char *expdest;
5053/* list of back quote expressions */
5054static struct nodelist *argbackq;
5055/* first struct in list of ifs regions */
5056static struct ifsregion ifsfirst;
5057/* last struct in list */
5058static struct ifsregion *ifslastp;
5059/* holds expanded arg list */
5060static struct arglist exparg;
5061
5062/*
5063 * Our own itoa().
5064 */
5065static int
5066cvtnum(arith_t num)
5067{
5068 int len;
5069
5070 expdest = makestrspace(32, expdest);
5071#if ENABLE_ASH_MATH_SUPPORT_64
5072 len = fmtstr(expdest, 32, "%lld", (long long) num);
5073#else
5074 len = fmtstr(expdest, 32, "%ld", num);
5075#endif
5076 STADJUST(len, expdest);
5077 return len;
5078}
5079
5080static size_t
5081esclen(const char *start, const char *p)
5082{
5083 size_t esc = 0;
5084
5085 while (p > start && *--p == CTLESC) {
5086 esc++;
5087 }
5088 return esc;
5089}
5090
5091/*
5092 * Remove any CTLESC characters from a string.
5093 */
5094static char *
5095_rmescapes(char *str, int flag)
5096{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005097 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005098
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005099 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005100 unsigned inquotes;
5101 int notescaped;
5102 int globbing;
5103
5104 p = strpbrk(str, qchars);
5105 if (!p) {
5106 return str;
5107 }
5108 q = p;
5109 r = str;
5110 if (flag & RMESCAPE_ALLOC) {
5111 size_t len = p - str;
5112 size_t fulllen = len + strlen(p) + 1;
5113
5114 if (flag & RMESCAPE_GROW) {
5115 r = makestrspace(fulllen, expdest);
5116 } else if (flag & RMESCAPE_HEAP) {
5117 r = ckmalloc(fulllen);
5118 } else {
5119 r = stalloc(fulllen);
5120 }
5121 q = r;
5122 if (len > 0) {
5123 q = memcpy(q, str, len) + len;
5124 }
5125 }
5126 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5127 globbing = flag & RMESCAPE_GLOB;
5128 notescaped = globbing;
5129 while (*p) {
5130 if (*p == CTLQUOTEMARK) {
5131 inquotes = ~inquotes;
5132 p++;
5133 notescaped = globbing;
5134 continue;
5135 }
5136 if (*p == '\\') {
5137 /* naked back slash */
5138 notescaped = 0;
5139 goto copy;
5140 }
5141 if (*p == CTLESC) {
5142 p++;
5143 if (notescaped && inquotes && *p != '/') {
5144 *q++ = '\\';
5145 }
5146 }
5147 notescaped = globbing;
5148 copy:
5149 *q++ = *p++;
5150 }
5151 *q = '\0';
5152 if (flag & RMESCAPE_GROW) {
5153 expdest = r;
5154 STADJUST(q - r + 1, expdest);
5155 }
5156 return r;
5157}
5158#define rmescapes(p) _rmescapes((p), 0)
5159
5160#define pmatch(a, b) !fnmatch((a), (b), 0)
5161
5162/*
5163 * Prepare a pattern for a expmeta (internal glob(3)) call.
5164 *
5165 * Returns an stalloced string.
5166 */
5167static char *
5168preglob(const char *pattern, int quoted, int flag)
5169{
5170 flag |= RMESCAPE_GLOB;
5171 if (quoted) {
5172 flag |= RMESCAPE_QUOTED;
5173 }
5174 return _rmescapes((char *)pattern, flag);
5175}
5176
5177/*
5178 * Put a string on the stack.
5179 */
5180static void
5181memtodest(const char *p, size_t len, int syntax, int quotes)
5182{
5183 char *q = expdest;
5184
5185 q = makestrspace(len * 2, q);
5186
5187 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005188 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005189 if (!c)
5190 continue;
5191 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5192 USTPUTC(CTLESC, q);
5193 USTPUTC(c, q);
5194 }
5195
5196 expdest = q;
5197}
5198
5199static void
5200strtodest(const char *p, int syntax, int quotes)
5201{
5202 memtodest(p, strlen(p), syntax, quotes);
5203}
5204
5205/*
5206 * Record the fact that we have to scan this region of the
5207 * string for IFS characters.
5208 */
5209static void
5210recordregion(int start, int end, int nulonly)
5211{
5212 struct ifsregion *ifsp;
5213
5214 if (ifslastp == NULL) {
5215 ifsp = &ifsfirst;
5216 } else {
5217 INT_OFF;
5218 ifsp = ckmalloc(sizeof(*ifsp));
5219 ifsp->next = NULL;
5220 ifslastp->next = ifsp;
5221 INT_ON;
5222 }
5223 ifslastp = ifsp;
5224 ifslastp->begoff = start;
5225 ifslastp->endoff = end;
5226 ifslastp->nulonly = nulonly;
5227}
5228
5229static void
5230removerecordregions(int endoff)
5231{
5232 if (ifslastp == NULL)
5233 return;
5234
5235 if (ifsfirst.endoff > endoff) {
5236 while (ifsfirst.next != NULL) {
5237 struct ifsregion *ifsp;
5238 INT_OFF;
5239 ifsp = ifsfirst.next->next;
5240 free(ifsfirst.next);
5241 ifsfirst.next = ifsp;
5242 INT_ON;
5243 }
5244 if (ifsfirst.begoff > endoff)
5245 ifslastp = NULL;
5246 else {
5247 ifslastp = &ifsfirst;
5248 ifsfirst.endoff = endoff;
5249 }
5250 return;
5251 }
5252
5253 ifslastp = &ifsfirst;
5254 while (ifslastp->next && ifslastp->next->begoff < endoff)
5255 ifslastp=ifslastp->next;
5256 while (ifslastp->next != NULL) {
5257 struct ifsregion *ifsp;
5258 INT_OFF;
5259 ifsp = ifslastp->next->next;
5260 free(ifslastp->next);
5261 ifslastp->next = ifsp;
5262 INT_ON;
5263 }
5264 if (ifslastp->endoff > endoff)
5265 ifslastp->endoff = endoff;
5266}
5267
5268static char *
5269exptilde(char *startp, char *p, int flag)
5270{
5271 char c;
5272 char *name;
5273 struct passwd *pw;
5274 const char *home;
5275 int quotes = flag & (EXP_FULL | EXP_CASE);
5276 int startloc;
5277
5278 name = p + 1;
5279
5280 while ((c = *++p) != '\0') {
5281 switch (c) {
5282 case CTLESC:
5283 return startp;
5284 case CTLQUOTEMARK:
5285 return startp;
5286 case ':':
5287 if (flag & EXP_VARTILDE)
5288 goto done;
5289 break;
5290 case '/':
5291 case CTLENDVAR:
5292 goto done;
5293 }
5294 }
5295 done:
5296 *p = '\0';
5297 if (*name == '\0') {
5298 home = lookupvar(homestr);
5299 } else {
5300 pw = getpwnam(name);
5301 if (pw == NULL)
5302 goto lose;
5303 home = pw->pw_dir;
5304 }
5305 if (!home || !*home)
5306 goto lose;
5307 *p = c;
5308 startloc = expdest - (char *)stackblock();
5309 strtodest(home, SQSYNTAX, quotes);
5310 recordregion(startloc, expdest - (char *)stackblock(), 0);
5311 return p;
5312 lose:
5313 *p = c;
5314 return startp;
5315}
5316
5317/*
5318 * Execute a command inside back quotes. If it's a builtin command, we
5319 * want to save its output in a block obtained from malloc. Otherwise
5320 * we fork off a subprocess and get the output of the command via a pipe.
5321 * Should be called with interrupts off.
5322 */
5323struct backcmd { /* result of evalbackcmd */
5324 int fd; /* file descriptor to read from */
5325 char *buf; /* buffer */
5326 int nleft; /* number of chars in buffer */
5327 struct job *jp; /* job structure for command */
5328};
5329
5330/* These forward decls are needed to use "eval" code for backticks handling: */
5331static int back_exitstatus; /* exit status of backquoted command */
5332#define EV_EXIT 01 /* exit after evaluating tree */
5333static void evaltree(union node *, int);
5334
5335static void
5336evalbackcmd(union node *n, struct backcmd *result)
5337{
5338 int saveherefd;
5339
5340 result->fd = -1;
5341 result->buf = NULL;
5342 result->nleft = 0;
5343 result->jp = NULL;
5344 if (n == NULL) {
5345 goto out;
5346 }
5347
5348 saveherefd = herefd;
5349 herefd = -1;
5350
5351 {
5352 int pip[2];
5353 struct job *jp;
5354
5355 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005356 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005357 jp = makejob(n, 1);
5358 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5359 FORCE_INT_ON;
5360 close(pip[0]);
5361 if (pip[1] != 1) {
5362 close(1);
5363 copyfd(pip[1], 1);
5364 close(pip[1]);
5365 }
5366 eflag = 0;
5367 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5368 /* NOTREACHED */
5369 }
5370 close(pip[1]);
5371 result->fd = pip[0];
5372 result->jp = jp;
5373 }
5374 herefd = saveherefd;
5375 out:
5376 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5377 result->fd, result->buf, result->nleft, result->jp));
5378}
5379
5380/*
5381 * Expand stuff in backwards quotes.
5382 */
5383static void
5384expbackq(union node *cmd, int quoted, int quotes)
5385{
5386 struct backcmd in;
5387 int i;
5388 char buf[128];
5389 char *p;
5390 char *dest;
5391 int startloc;
5392 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5393 struct stackmark smark;
5394
5395 INT_OFF;
5396 setstackmark(&smark);
5397 dest = expdest;
5398 startloc = dest - (char *)stackblock();
5399 grabstackstr(dest);
5400 evalbackcmd(cmd, &in);
5401 popstackmark(&smark);
5402
5403 p = in.buf;
5404 i = in.nleft;
5405 if (i == 0)
5406 goto read;
5407 for (;;) {
5408 memtodest(p, i, syntax, quotes);
5409 read:
5410 if (in.fd < 0)
5411 break;
5412 i = safe_read(in.fd, buf, sizeof(buf));
5413 TRACE(("expbackq: read returns %d\n", i));
5414 if (i <= 0)
5415 break;
5416 p = buf;
5417 }
5418
Denis Vlasenko60818682007-09-28 22:07:23 +00005419 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005420 if (in.fd >= 0) {
5421 close(in.fd);
5422 back_exitstatus = waitforjob(in.jp);
5423 }
5424 INT_ON;
5425
5426 /* Eat all trailing newlines */
5427 dest = expdest;
5428 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5429 STUNPUTC(dest);
5430 expdest = dest;
5431
5432 if (quoted == 0)
5433 recordregion(startloc, dest - (char *)stackblock(), 0);
5434 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5435 (dest - (char *)stackblock()) - startloc,
5436 (dest - (char *)stackblock()) - startloc,
5437 stackblock() + startloc));
5438}
5439
5440#if ENABLE_ASH_MATH_SUPPORT
5441/*
5442 * Expand arithmetic expression. Backup to start of expression,
5443 * evaluate, place result in (backed up) result, adjust string position.
5444 */
5445static void
5446expari(int quotes)
5447{
5448 char *p, *start;
5449 int begoff;
5450 int flag;
5451 int len;
5452
5453 /* ifsfree(); */
5454
5455 /*
5456 * This routine is slightly over-complicated for
5457 * efficiency. Next we scan backwards looking for the
5458 * start of arithmetic.
5459 */
5460 start = stackblock();
5461 p = expdest - 1;
5462 *p = '\0';
5463 p--;
5464 do {
5465 int esc;
5466
5467 while (*p != CTLARI) {
5468 p--;
5469#if DEBUG
5470 if (p < start) {
5471 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5472 }
5473#endif
5474 }
5475
5476 esc = esclen(start, p);
5477 if (!(esc % 2)) {
5478 break;
5479 }
5480
5481 p -= esc + 1;
5482 } while (1);
5483
5484 begoff = p - start;
5485
5486 removerecordregions(begoff);
5487
5488 flag = p[1];
5489
5490 expdest = p;
5491
5492 if (quotes)
5493 rmescapes(p + 2);
5494
5495 len = cvtnum(dash_arith(p + 2));
5496
5497 if (flag != '"')
5498 recordregion(begoff, begoff + len, 0);
5499}
5500#endif
5501
5502/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005503static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005504
5505/*
5506 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5507 * characters to allow for further processing. Otherwise treat
5508 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005509 *
5510 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5511 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5512 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005513 */
5514static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005515argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005516{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005517 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005518 '=',
5519 ':',
5520 CTLQUOTEMARK,
5521 CTLENDVAR,
5522 CTLESC,
5523 CTLVAR,
5524 CTLBACKQ,
5525 CTLBACKQ | CTLQUOTE,
5526#if ENABLE_ASH_MATH_SUPPORT
5527 CTLENDARI,
5528#endif
5529 0
5530 };
5531 const char *reject = spclchars;
5532 int c;
5533 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5534 int breakall = flag & EXP_WORD;
5535 int inquotes;
5536 size_t length;
5537 int startloc;
5538
5539 if (!(flag & EXP_VARTILDE)) {
5540 reject += 2;
5541 } else if (flag & EXP_VARTILDE2) {
5542 reject++;
5543 }
5544 inquotes = 0;
5545 length = 0;
5546 if (flag & EXP_TILDE) {
5547 char *q;
5548
5549 flag &= ~EXP_TILDE;
5550 tilde:
5551 q = p;
5552 if (*q == CTLESC && (flag & EXP_QWORD))
5553 q++;
5554 if (*q == '~')
5555 p = exptilde(p, q, flag);
5556 }
5557 start:
5558 startloc = expdest - (char *)stackblock();
5559 for (;;) {
5560 length += strcspn(p + length, reject);
5561 c = p[length];
5562 if (c && (!(c & 0x80)
5563#if ENABLE_ASH_MATH_SUPPORT
5564 || c == CTLENDARI
5565#endif
5566 )) {
5567 /* c == '=' || c == ':' || c == CTLENDARI */
5568 length++;
5569 }
5570 if (length > 0) {
5571 int newloc;
5572 expdest = stack_nputstr(p, length, expdest);
5573 newloc = expdest - (char *)stackblock();
5574 if (breakall && !inquotes && newloc > startloc) {
5575 recordregion(startloc, newloc, 0);
5576 }
5577 startloc = newloc;
5578 }
5579 p += length + 1;
5580 length = 0;
5581
5582 switch (c) {
5583 case '\0':
5584 goto breakloop;
5585 case '=':
5586 if (flag & EXP_VARTILDE2) {
5587 p--;
5588 continue;
5589 }
5590 flag |= EXP_VARTILDE2;
5591 reject++;
5592 /* fall through */
5593 case ':':
5594 /*
5595 * sort of a hack - expand tildes in variable
5596 * assignments (after the first '=' and after ':'s).
5597 */
5598 if (*--p == '~') {
5599 goto tilde;
5600 }
5601 continue;
5602 }
5603
5604 switch (c) {
5605 case CTLENDVAR: /* ??? */
5606 goto breakloop;
5607 case CTLQUOTEMARK:
5608 /* "$@" syntax adherence hack */
5609 if (
5610 !inquotes &&
5611 !memcmp(p, dolatstr, 4) &&
5612 (p[4] == CTLQUOTEMARK || (
5613 p[4] == CTLENDVAR &&
5614 p[5] == CTLQUOTEMARK
5615 ))
5616 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005617 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005618 goto start;
5619 }
5620 inquotes = !inquotes;
5621 addquote:
5622 if (quotes) {
5623 p--;
5624 length++;
5625 startloc++;
5626 }
5627 break;
5628 case CTLESC:
5629 startloc++;
5630 length++;
5631 goto addquote;
5632 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005633 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005634 goto start;
5635 case CTLBACKQ:
5636 c = 0;
5637 case CTLBACKQ|CTLQUOTE:
5638 expbackq(argbackq->n, c, quotes);
5639 argbackq = argbackq->next;
5640 goto start;
5641#if ENABLE_ASH_MATH_SUPPORT
5642 case CTLENDARI:
5643 p--;
5644 expari(quotes);
5645 goto start;
5646#endif
5647 }
5648 }
5649 breakloop:
5650 ;
5651}
5652
5653static char *
5654scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5655 int zero)
5656{
5657 char *loc;
5658 char *loc2;
5659 char c;
5660
5661 loc = startp;
5662 loc2 = rmesc;
5663 do {
5664 int match;
5665 const char *s = loc2;
5666 c = *loc2;
5667 if (zero) {
5668 *loc2 = '\0';
5669 s = rmesc;
5670 }
5671 match = pmatch(str, s);
5672 *loc2 = c;
5673 if (match)
5674 return loc;
5675 if (quotes && *loc == CTLESC)
5676 loc++;
5677 loc++;
5678 loc2++;
5679 } while (c);
5680 return 0;
5681}
5682
5683static char *
5684scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5685 int zero)
5686{
5687 int esc = 0;
5688 char *loc;
5689 char *loc2;
5690
5691 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5692 int match;
5693 char c = *loc2;
5694 const char *s = loc2;
5695 if (zero) {
5696 *loc2 = '\0';
5697 s = rmesc;
5698 }
5699 match = pmatch(str, s);
5700 *loc2 = c;
5701 if (match)
5702 return loc;
5703 loc--;
5704 if (quotes) {
5705 if (--esc < 0) {
5706 esc = esclen(startp, loc);
5707 }
5708 if (esc % 2) {
5709 esc--;
5710 loc--;
5711 }
5712 }
5713 }
5714 return 0;
5715}
5716
5717static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5718static void
5719varunset(const char *end, const char *var, const char *umsg, int varflags)
5720{
5721 const char *msg;
5722 const char *tail;
5723
5724 tail = nullstr;
5725 msg = "parameter not set";
5726 if (umsg) {
5727 if (*end == CTLENDVAR) {
5728 if (varflags & VSNUL)
5729 tail = " or null";
5730 } else
5731 msg = umsg;
5732 }
5733 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5734}
5735
5736static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005737subevalvar(char *p, char *str, int strloc, int subtype,
5738 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005739{
5740 char *startp;
5741 char *loc;
5742 int saveherefd = herefd;
5743 struct nodelist *saveargbackq = argbackq;
5744 int amount;
5745 char *rmesc, *rmescend;
5746 int zero;
5747 char *(*scan)(char *, char *, char *, char *, int , int);
5748
5749 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005750 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5751 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005752 STPUTC('\0', expdest);
5753 herefd = saveherefd;
5754 argbackq = saveargbackq;
5755 startp = stackblock() + startloc;
5756
5757 switch (subtype) {
5758 case VSASSIGN:
5759 setvar(str, startp, 0);
5760 amount = startp - expdest;
5761 STADJUST(amount, expdest);
5762 return startp;
5763
5764 case VSQUESTION:
5765 varunset(p, str, startp, varflags);
5766 /* NOTREACHED */
5767 }
5768
5769 subtype -= VSTRIMRIGHT;
5770#if DEBUG
5771 if (subtype < 0 || subtype > 3)
5772 abort();
5773#endif
5774
5775 rmesc = startp;
5776 rmescend = stackblock() + strloc;
5777 if (quotes) {
5778 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5779 if (rmesc != startp) {
5780 rmescend = expdest;
5781 startp = stackblock() + startloc;
5782 }
5783 }
5784 rmescend--;
5785 str = stackblock() + strloc;
5786 preglob(str, varflags & VSQUOTE, 0);
5787
5788 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5789 zero = subtype >> 1;
5790 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5791 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5792
5793 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5794 if (loc) {
5795 if (zero) {
5796 memmove(startp, loc, str - loc);
5797 loc = startp + (str - loc) - 1;
5798 }
5799 *loc = '\0';
5800 amount = loc - expdest;
5801 STADJUST(amount, expdest);
5802 }
5803 return loc;
5804}
5805
5806/*
5807 * Add the value of a specialized variable to the stack string.
5808 */
5809static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005810varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005811{
5812 int num;
5813 char *p;
5814 int i;
5815 int sep = 0;
5816 int sepq = 0;
5817 ssize_t len = 0;
5818 char **ap;
5819 int syntax;
5820 int quoted = varflags & VSQUOTE;
5821 int subtype = varflags & VSTYPE;
5822 int quotes = flags & (EXP_FULL | EXP_CASE);
5823
5824 if (quoted && (flags & EXP_FULL))
5825 sep = 1 << CHAR_BIT;
5826
5827 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5828 switch (*name) {
5829 case '$':
5830 num = rootpid;
5831 goto numvar;
5832 case '?':
5833 num = exitstatus;
5834 goto numvar;
5835 case '#':
5836 num = shellparam.nparam;
5837 goto numvar;
5838 case '!':
5839 num = backgndpid;
5840 if (num == 0)
5841 return -1;
5842 numvar:
5843 len = cvtnum(num);
5844 break;
5845 case '-':
5846 p = makestrspace(NOPTS, expdest);
5847 for (i = NOPTS - 1; i >= 0; i--) {
5848 if (optlist[i]) {
5849 USTPUTC(optletters(i), p);
5850 len++;
5851 }
5852 }
5853 expdest = p;
5854 break;
5855 case '@':
5856 if (sep)
5857 goto param;
5858 /* fall through */
5859 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005860 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005861 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5862 sepq = 1;
5863 param:
5864 ap = shellparam.p;
5865 if (!ap)
5866 return -1;
5867 while ((p = *ap++)) {
5868 size_t partlen;
5869
5870 partlen = strlen(p);
5871 len += partlen;
5872
5873 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5874 memtodest(p, partlen, syntax, quotes);
5875
5876 if (*ap && sep) {
5877 char *q;
5878
5879 len++;
5880 if (subtype == VSPLUS || subtype == VSLENGTH) {
5881 continue;
5882 }
5883 q = expdest;
5884 if (sepq)
5885 STPUTC(CTLESC, q);
5886 STPUTC(sep, q);
5887 expdest = q;
5888 }
5889 }
5890 return len;
5891 case '0':
5892 case '1':
5893 case '2':
5894 case '3':
5895 case '4':
5896 case '5':
5897 case '6':
5898 case '7':
5899 case '8':
5900 case '9':
5901 num = atoi(name);
5902 if (num < 0 || num > shellparam.nparam)
5903 return -1;
5904 p = num ? shellparam.p[num - 1] : arg0;
5905 goto value;
5906 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005907 /* NB: name has form "VAR=..." */
5908
5909 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
5910 * which should be considered before we check variables. */
5911 if (var_str_list) {
5912 unsigned name_len = (strchrnul(name, '=') - name) + 1;
5913 p = NULL;
5914 do {
5915 char *str = var_str_list->text;
5916 char *eq = strchr(str, '=');
5917 if (!eq) /* stop at first non-assignment */
5918 break;
5919 eq++;
5920 if (name_len == (eq - str)
5921 && strncmp(str, name, name_len) == 0) {
5922 p = eq;
5923 /* goto value; - WRONG! */
5924 /* think "A=1 A=2 B=$A" */
5925 }
5926 var_str_list = var_str_list->next;
5927 } while (var_str_list);
5928 if (p)
5929 goto value;
5930 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005931 p = lookupvar(name);
5932 value:
5933 if (!p)
5934 return -1;
5935
5936 len = strlen(p);
5937 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5938 memtodest(p, len, syntax, quotes);
5939 return len;
5940 }
5941
5942 if (subtype == VSPLUS || subtype == VSLENGTH)
5943 STADJUST(-len, expdest);
5944 return len;
5945}
5946
5947/*
5948 * Expand a variable, and return a pointer to the next character in the
5949 * input string.
5950 */
5951static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005952evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005953{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005954 char varflags;
5955 char subtype;
5956 char quoted;
5957 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958 char *var;
5959 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005960 int startloc;
5961 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005962
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963 varflags = *p++;
5964 subtype = varflags & VSTYPE;
5965 quoted = varflags & VSQUOTE;
5966 var = p;
5967 easy = (!quoted || (*var == '@' && shellparam.nparam));
5968 startloc = expdest - (char *)stackblock();
5969 p = strchr(p, '=') + 1;
5970
5971 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005972 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005973 if (varflags & VSNUL)
5974 varlen--;
5975
5976 if (subtype == VSPLUS) {
5977 varlen = -1 - varlen;
5978 goto vsplus;
5979 }
5980
5981 if (subtype == VSMINUS) {
5982 vsplus:
5983 if (varlen < 0) {
5984 argstr(
5985 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005986 (quoted ? EXP_QWORD : EXP_WORD),
5987 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 );
5989 goto end;
5990 }
5991 if (easy)
5992 goto record;
5993 goto end;
5994 }
5995
5996 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5997 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005998 if (subevalvar(p, var, /* strloc: */ 0,
5999 subtype, startloc, varflags,
6000 /* quotes: */ 0,
6001 var_str_list)
6002 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003 varflags &= ~VSNUL;
6004 /*
6005 * Remove any recorded regions beyond
6006 * start of variable
6007 */
6008 removerecordregions(startloc);
6009 goto again;
6010 }
6011 goto end;
6012 }
6013 if (easy)
6014 goto record;
6015 goto end;
6016 }
6017
6018 if (varlen < 0 && uflag)
6019 varunset(p, var, 0, 0);
6020
6021 if (subtype == VSLENGTH) {
6022 cvtnum(varlen > 0 ? varlen : 0);
6023 goto record;
6024 }
6025
6026 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006027 if (easy)
6028 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029 goto end;
6030 }
6031
6032#if DEBUG
6033 switch (subtype) {
6034 case VSTRIMLEFT:
6035 case VSTRIMLEFTMAX:
6036 case VSTRIMRIGHT:
6037 case VSTRIMRIGHTMAX:
6038 break;
6039 default:
6040 abort();
6041 }
6042#endif
6043
6044 if (varlen >= 0) {
6045 /*
6046 * Terminate the string and start recording the pattern
6047 * right after it
6048 */
6049 STPUTC('\0', expdest);
6050 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006051 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6052 startloc, varflags,
6053 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6054 var_str_list)
6055 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006056 int amount = expdest - (
6057 (char *)stackblock() + patloc - 1
6058 );
6059 STADJUST(-amount, expdest);
6060 }
6061 /* Remove any recorded regions beyond start of variable */
6062 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006063 record:
6064 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 }
6066
6067 end:
6068 if (subtype != VSNORMAL) { /* skip to end of alternative */
6069 int nesting = 1;
6070 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006071 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 if (c == CTLESC)
6073 p++;
6074 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6075 if (varlen >= 0)
6076 argbackq = argbackq->next;
6077 } else if (c == CTLVAR) {
6078 if ((*p++ & VSTYPE) != VSNORMAL)
6079 nesting++;
6080 } else if (c == CTLENDVAR) {
6081 if (--nesting == 0)
6082 break;
6083 }
6084 }
6085 }
6086 return p;
6087}
6088
6089/*
6090 * Break the argument string into pieces based upon IFS and add the
6091 * strings to the argument list. The regions of the string to be
6092 * searched for IFS characters have been stored by recordregion.
6093 */
6094static void
6095ifsbreakup(char *string, struct arglist *arglist)
6096{
6097 struct ifsregion *ifsp;
6098 struct strlist *sp;
6099 char *start;
6100 char *p;
6101 char *q;
6102 const char *ifs, *realifs;
6103 int ifsspc;
6104 int nulonly;
6105
6106 start = string;
6107 if (ifslastp != NULL) {
6108 ifsspc = 0;
6109 nulonly = 0;
6110 realifs = ifsset() ? ifsval() : defifs;
6111 ifsp = &ifsfirst;
6112 do {
6113 p = string + ifsp->begoff;
6114 nulonly = ifsp->nulonly;
6115 ifs = nulonly ? nullstr : realifs;
6116 ifsspc = 0;
6117 while (p < string + ifsp->endoff) {
6118 q = p;
6119 if (*p == CTLESC)
6120 p++;
6121 if (!strchr(ifs, *p)) {
6122 p++;
6123 continue;
6124 }
6125 if (!nulonly)
6126 ifsspc = (strchr(defifs, *p) != NULL);
6127 /* Ignore IFS whitespace at start */
6128 if (q == start && ifsspc) {
6129 p++;
6130 start = p;
6131 continue;
6132 }
6133 *q = '\0';
6134 sp = stalloc(sizeof(*sp));
6135 sp->text = start;
6136 *arglist->lastp = sp;
6137 arglist->lastp = &sp->next;
6138 p++;
6139 if (!nulonly) {
6140 for (;;) {
6141 if (p >= string + ifsp->endoff) {
6142 break;
6143 }
6144 q = p;
6145 if (*p == CTLESC)
6146 p++;
6147 if (strchr(ifs, *p) == NULL ) {
6148 p = q;
6149 break;
6150 } else if (strchr(defifs, *p) == NULL) {
6151 if (ifsspc) {
6152 p++;
6153 ifsspc = 0;
6154 } else {
6155 p = q;
6156 break;
6157 }
6158 } else
6159 p++;
6160 }
6161 }
6162 start = p;
6163 } /* while */
6164 ifsp = ifsp->next;
6165 } while (ifsp != NULL);
6166 if (nulonly)
6167 goto add;
6168 }
6169
6170 if (!*start)
6171 return;
6172
6173 add:
6174 sp = stalloc(sizeof(*sp));
6175 sp->text = start;
6176 *arglist->lastp = sp;
6177 arglist->lastp = &sp->next;
6178}
6179
6180static void
6181ifsfree(void)
6182{
6183 struct ifsregion *p;
6184
6185 INT_OFF;
6186 p = ifsfirst.next;
6187 do {
6188 struct ifsregion *ifsp;
6189 ifsp = p->next;
6190 free(p);
6191 p = ifsp;
6192 } while (p);
6193 ifslastp = NULL;
6194 ifsfirst.next = NULL;
6195 INT_ON;
6196}
6197
6198/*
6199 * Add a file name to the list.
6200 */
6201static void
6202addfname(const char *name)
6203{
6204 struct strlist *sp;
6205
6206 sp = stalloc(sizeof(*sp));
6207 sp->text = ststrdup(name);
6208 *exparg.lastp = sp;
6209 exparg.lastp = &sp->next;
6210}
6211
6212static char *expdir;
6213
6214/*
6215 * Do metacharacter (i.e. *, ?, [...]) expansion.
6216 */
6217static void
6218expmeta(char *enddir, char *name)
6219{
6220 char *p;
6221 const char *cp;
6222 char *start;
6223 char *endname;
6224 int metaflag;
6225 struct stat statb;
6226 DIR *dirp;
6227 struct dirent *dp;
6228 int atend;
6229 int matchdot;
6230
6231 metaflag = 0;
6232 start = name;
6233 for (p = name; *p; p++) {
6234 if (*p == '*' || *p == '?')
6235 metaflag = 1;
6236 else if (*p == '[') {
6237 char *q = p + 1;
6238 if (*q == '!')
6239 q++;
6240 for (;;) {
6241 if (*q == '\\')
6242 q++;
6243 if (*q == '/' || *q == '\0')
6244 break;
6245 if (*++q == ']') {
6246 metaflag = 1;
6247 break;
6248 }
6249 }
6250 } else if (*p == '\\')
6251 p++;
6252 else if (*p == '/') {
6253 if (metaflag)
6254 goto out;
6255 start = p + 1;
6256 }
6257 }
6258 out:
6259 if (metaflag == 0) { /* we've reached the end of the file name */
6260 if (enddir != expdir)
6261 metaflag++;
6262 p = name;
6263 do {
6264 if (*p == '\\')
6265 p++;
6266 *enddir++ = *p;
6267 } while (*p++);
6268 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6269 addfname(expdir);
6270 return;
6271 }
6272 endname = p;
6273 if (name < start) {
6274 p = name;
6275 do {
6276 if (*p == '\\')
6277 p++;
6278 *enddir++ = *p++;
6279 } while (p < start);
6280 }
6281 if (enddir == expdir) {
6282 cp = ".";
6283 } else if (enddir == expdir + 1 && *expdir == '/') {
6284 cp = "/";
6285 } else {
6286 cp = expdir;
6287 enddir[-1] = '\0';
6288 }
6289 dirp = opendir(cp);
6290 if (dirp == NULL)
6291 return;
6292 if (enddir != expdir)
6293 enddir[-1] = '/';
6294 if (*endname == 0) {
6295 atend = 1;
6296 } else {
6297 atend = 0;
6298 *endname++ = '\0';
6299 }
6300 matchdot = 0;
6301 p = start;
6302 if (*p == '\\')
6303 p++;
6304 if (*p == '.')
6305 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006306 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006307 if (dp->d_name[0] == '.' && ! matchdot)
6308 continue;
6309 if (pmatch(start, dp->d_name)) {
6310 if (atend) {
6311 strcpy(enddir, dp->d_name);
6312 addfname(expdir);
6313 } else {
6314 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6315 continue;
6316 p[-1] = '/';
6317 expmeta(p, endname);
6318 }
6319 }
6320 }
6321 closedir(dirp);
6322 if (! atend)
6323 endname[-1] = '/';
6324}
6325
6326static struct strlist *
6327msort(struct strlist *list, int len)
6328{
6329 struct strlist *p, *q = NULL;
6330 struct strlist **lpp;
6331 int half;
6332 int n;
6333
6334 if (len <= 1)
6335 return list;
6336 half = len >> 1;
6337 p = list;
6338 for (n = half; --n >= 0; ) {
6339 q = p;
6340 p = p->next;
6341 }
6342 q->next = NULL; /* terminate first half of list */
6343 q = msort(list, half); /* sort first half of list */
6344 p = msort(p, len - half); /* sort second half */
6345 lpp = &list;
6346 for (;;) {
6347#if ENABLE_LOCALE_SUPPORT
6348 if (strcoll(p->text, q->text) < 0)
6349#else
6350 if (strcmp(p->text, q->text) < 0)
6351#endif
6352 {
6353 *lpp = p;
6354 lpp = &p->next;
6355 p = *lpp;
6356 if (p == NULL) {
6357 *lpp = q;
6358 break;
6359 }
6360 } else {
6361 *lpp = q;
6362 lpp = &q->next;
6363 q = *lpp;
6364 if (q == NULL) {
6365 *lpp = p;
6366 break;
6367 }
6368 }
6369 }
6370 return list;
6371}
6372
6373/*
6374 * Sort the results of file name expansion. It calculates the number of
6375 * strings to sort and then calls msort (short for merge sort) to do the
6376 * work.
6377 */
6378static struct strlist *
6379expsort(struct strlist *str)
6380{
6381 int len;
6382 struct strlist *sp;
6383
6384 len = 0;
6385 for (sp = str; sp; sp = sp->next)
6386 len++;
6387 return msort(str, len);
6388}
6389
6390static void
6391expandmeta(struct strlist *str, int flag)
6392{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006393 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006394 '*', '?', '[', 0
6395 };
6396 /* TODO - EXP_REDIR */
6397
6398 while (str) {
6399 struct strlist **savelastp;
6400 struct strlist *sp;
6401 char *p;
6402
6403 if (fflag)
6404 goto nometa;
6405 if (!strpbrk(str->text, metachars))
6406 goto nometa;
6407 savelastp = exparg.lastp;
6408
6409 INT_OFF;
6410 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6411 {
6412 int i = strlen(str->text);
6413 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6414 }
6415
6416 expmeta(expdir, p);
6417 free(expdir);
6418 if (p != str->text)
6419 free(p);
6420 INT_ON;
6421 if (exparg.lastp == savelastp) {
6422 /*
6423 * no matches
6424 */
6425 nometa:
6426 *exparg.lastp = str;
6427 rmescapes(str->text);
6428 exparg.lastp = &str->next;
6429 } else {
6430 *exparg.lastp = NULL;
6431 *savelastp = sp = expsort(*savelastp);
6432 while (sp->next != NULL)
6433 sp = sp->next;
6434 exparg.lastp = &sp->next;
6435 }
6436 str = str->next;
6437 }
6438}
6439
6440/*
6441 * Perform variable substitution and command substitution on an argument,
6442 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6443 * perform splitting and file name expansion. When arglist is NULL, perform
6444 * here document expansion.
6445 */
6446static void
6447expandarg(union node *arg, struct arglist *arglist, int flag)
6448{
6449 struct strlist *sp;
6450 char *p;
6451
6452 argbackq = arg->narg.backquote;
6453 STARTSTACKSTR(expdest);
6454 ifsfirst.next = NULL;
6455 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006456 argstr(arg->narg.text, flag,
6457 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006458 p = _STPUTC('\0', expdest);
6459 expdest = p - 1;
6460 if (arglist == NULL) {
6461 return; /* here document expanded */
6462 }
6463 p = grabstackstr(p);
6464 exparg.lastp = &exparg.list;
6465 /*
6466 * TODO - EXP_REDIR
6467 */
6468 if (flag & EXP_FULL) {
6469 ifsbreakup(p, &exparg);
6470 *exparg.lastp = NULL;
6471 exparg.lastp = &exparg.list;
6472 expandmeta(exparg.list, flag);
6473 } else {
6474 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6475 rmescapes(p);
6476 sp = stalloc(sizeof(*sp));
6477 sp->text = p;
6478 *exparg.lastp = sp;
6479 exparg.lastp = &sp->next;
6480 }
6481 if (ifsfirst.next)
6482 ifsfree();
6483 *exparg.lastp = NULL;
6484 if (exparg.list) {
6485 *arglist->lastp = exparg.list;
6486 arglist->lastp = exparg.lastp;
6487 }
6488}
6489
6490/*
6491 * Expand shell variables and backquotes inside a here document.
6492 */
6493static void
6494expandhere(union node *arg, int fd)
6495{
6496 herefd = fd;
6497 expandarg(arg, (struct arglist *)NULL, 0);
6498 full_write(fd, stackblock(), expdest - (char *)stackblock());
6499}
6500
6501/*
6502 * Returns true if the pattern matches the string.
6503 */
6504static int
6505patmatch(char *pattern, const char *string)
6506{
6507 return pmatch(preglob(pattern, 0, 0), string);
6508}
6509
6510/*
6511 * See if a pattern matches in a case statement.
6512 */
6513static int
6514casematch(union node *pattern, char *val)
6515{
6516 struct stackmark smark;
6517 int result;
6518
6519 setstackmark(&smark);
6520 argbackq = pattern->narg.backquote;
6521 STARTSTACKSTR(expdest);
6522 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006523 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6524 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006525 STACKSTRNUL(expdest);
6526 result = patmatch(stackblock(), val);
6527 popstackmark(&smark);
6528 return result;
6529}
6530
6531
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006532/* ============ find_command */
6533
6534struct builtincmd {
6535 const char *name;
6536 int (*builtin)(int, char **);
6537 /* unsigned flags; */
6538};
6539#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006540/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006541 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006542#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006543#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006544
6545struct cmdentry {
6546 int cmdtype;
6547 union param {
6548 int index;
6549 const struct builtincmd *cmd;
6550 struct funcnode *func;
6551 } u;
6552};
6553/* values of cmdtype */
6554#define CMDUNKNOWN -1 /* no entry in table for command */
6555#define CMDNORMAL 0 /* command is an executable program */
6556#define CMDFUNCTION 1 /* command is a shell function */
6557#define CMDBUILTIN 2 /* command is a shell builtin */
6558
6559/* action to find_command() */
6560#define DO_ERR 0x01 /* prints errors */
6561#define DO_ABS 0x02 /* checks absolute paths */
6562#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6563#define DO_ALTPATH 0x08 /* using alternate path */
6564#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6565
6566static void find_command(char *, struct cmdentry *, int, const char *);
6567
6568
6569/* ============ Hashing commands */
6570
6571/*
6572 * When commands are first encountered, they are entered in a hash table.
6573 * This ensures that a full path search will not have to be done for them
6574 * on each invocation.
6575 *
6576 * We should investigate converting to a linear search, even though that
6577 * would make the command name "hash" a misnomer.
6578 */
6579
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006580#define ARB 1 /* actual size determined at run time */
6581
6582struct tblentry {
6583 struct tblentry *next; /* next entry in hash chain */
6584 union param param; /* definition of builtin function */
6585 short cmdtype; /* index identifying command */
6586 char rehash; /* if set, cd done since entry created */
6587 char cmdname[ARB]; /* name of command */
6588};
6589
Denis Vlasenko01631112007-12-16 17:20:38 +00006590static struct tblentry **cmdtable;
6591#define INIT_G_cmdtable() do { \
6592 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6593} while (0)
6594
6595static int builtinloc = -1; /* index in path of %builtin, or -1 */
6596
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006597
6598static void
6599tryexec(char *cmd, char **argv, char **envp)
6600{
6601 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006602
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006603#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006604 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006605 int a = find_applet_by_name(cmd);
6606 if (a >= 0) {
6607 if (APPLET_IS_NOEXEC(a))
6608 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006609 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006610 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006611 /* If they called chroot or otherwise made the binary no longer
6612 * executable, fall through */
6613 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006614 }
6615#endif
6616
6617 repeat:
6618#ifdef SYSV
6619 do {
6620 execve(cmd, argv, envp);
6621 } while (errno == EINTR);
6622#else
6623 execve(cmd, argv, envp);
6624#endif
6625 if (repeated++) {
6626 free(argv);
6627 } else if (errno == ENOEXEC) {
6628 char **ap;
6629 char **new;
6630
6631 for (ap = argv; *ap; ap++)
6632 ;
6633 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6634 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006635 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006636 ap += 2;
6637 argv++;
6638 while ((*ap++ = *argv++))
6639 ;
6640 argv = new;
6641 goto repeat;
6642 }
6643}
6644
6645/*
6646 * Exec a program. Never returns. If you change this routine, you may
6647 * have to change the find_command routine as well.
6648 */
6649#define environment() listvars(VEXPORT, VUNSET, 0)
6650static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6651static void
6652shellexec(char **argv, const char *path, int idx)
6653{
6654 char *cmdname;
6655 int e;
6656 char **envp;
6657 int exerrno;
6658
6659 clearredir(1);
6660 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006661 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006662#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006663 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006664#endif
6665 ) {
6666 tryexec(argv[0], argv, envp);
6667 e = errno;
6668 } else {
6669 e = ENOENT;
6670 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6671 if (--idx < 0 && pathopt == NULL) {
6672 tryexec(cmdname, argv, envp);
6673 if (errno != ENOENT && errno != ENOTDIR)
6674 e = errno;
6675 }
6676 stunalloc(cmdname);
6677 }
6678 }
6679
6680 /* Map to POSIX errors */
6681 switch (e) {
6682 case EACCES:
6683 exerrno = 126;
6684 break;
6685 case ENOENT:
6686 exerrno = 127;
6687 break;
6688 default:
6689 exerrno = 2;
6690 break;
6691 }
6692 exitstatus = exerrno;
6693 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6694 argv[0], e, suppressint ));
6695 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6696 /* NOTREACHED */
6697}
6698
6699static void
6700printentry(struct tblentry *cmdp)
6701{
6702 int idx;
6703 const char *path;
6704 char *name;
6705
6706 idx = cmdp->param.index;
6707 path = pathval();
6708 do {
6709 name = padvance(&path, cmdp->cmdname);
6710 stunalloc(name);
6711 } while (--idx >= 0);
6712 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6713}
6714
6715/*
6716 * Clear out command entries. The argument specifies the first entry in
6717 * PATH which has changed.
6718 */
6719static void
6720clearcmdentry(int firstchange)
6721{
6722 struct tblentry **tblp;
6723 struct tblentry **pp;
6724 struct tblentry *cmdp;
6725
6726 INT_OFF;
6727 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6728 pp = tblp;
6729 while ((cmdp = *pp) != NULL) {
6730 if ((cmdp->cmdtype == CMDNORMAL &&
6731 cmdp->param.index >= firstchange)
6732 || (cmdp->cmdtype == CMDBUILTIN &&
6733 builtinloc >= firstchange)
6734 ) {
6735 *pp = cmdp->next;
6736 free(cmdp);
6737 } else {
6738 pp = &cmdp->next;
6739 }
6740 }
6741 }
6742 INT_ON;
6743}
6744
6745/*
6746 * Locate a command in the command hash table. If "add" is nonzero,
6747 * add the command to the table if it is not already present. The
6748 * variable "lastcmdentry" is set to point to the address of the link
6749 * pointing to the entry, so that delete_cmd_entry can delete the
6750 * entry.
6751 *
6752 * Interrupts must be off if called with add != 0.
6753 */
6754static struct tblentry **lastcmdentry;
6755
6756static struct tblentry *
6757cmdlookup(const char *name, int add)
6758{
6759 unsigned int hashval;
6760 const char *p;
6761 struct tblentry *cmdp;
6762 struct tblentry **pp;
6763
6764 p = name;
6765 hashval = (unsigned char)*p << 4;
6766 while (*p)
6767 hashval += (unsigned char)*p++;
6768 hashval &= 0x7FFF;
6769 pp = &cmdtable[hashval % CMDTABLESIZE];
6770 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6771 if (strcmp(cmdp->cmdname, name) == 0)
6772 break;
6773 pp = &cmdp->next;
6774 }
6775 if (add && cmdp == NULL) {
6776 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6777 + strlen(name) + 1);
6778 cmdp->next = NULL;
6779 cmdp->cmdtype = CMDUNKNOWN;
6780 strcpy(cmdp->cmdname, name);
6781 }
6782 lastcmdentry = pp;
6783 return cmdp;
6784}
6785
6786/*
6787 * Delete the command entry returned on the last lookup.
6788 */
6789static void
6790delete_cmd_entry(void)
6791{
6792 struct tblentry *cmdp;
6793
6794 INT_OFF;
6795 cmdp = *lastcmdentry;
6796 *lastcmdentry = cmdp->next;
6797 if (cmdp->cmdtype == CMDFUNCTION)
6798 freefunc(cmdp->param.func);
6799 free(cmdp);
6800 INT_ON;
6801}
6802
6803/*
6804 * Add a new command entry, replacing any existing command entry for
6805 * the same name - except special builtins.
6806 */
6807static void
6808addcmdentry(char *name, struct cmdentry *entry)
6809{
6810 struct tblentry *cmdp;
6811
6812 cmdp = cmdlookup(name, 1);
6813 if (cmdp->cmdtype == CMDFUNCTION) {
6814 freefunc(cmdp->param.func);
6815 }
6816 cmdp->cmdtype = entry->cmdtype;
6817 cmdp->param = entry->u;
6818 cmdp->rehash = 0;
6819}
6820
6821static int
6822hashcmd(int argc, char **argv)
6823{
6824 struct tblentry **pp;
6825 struct tblentry *cmdp;
6826 int c;
6827 struct cmdentry entry;
6828 char *name;
6829
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006830 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006831 clearcmdentry(0);
6832 return 0;
6833 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006834
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006835 if (*argptr == NULL) {
6836 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6837 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6838 if (cmdp->cmdtype == CMDNORMAL)
6839 printentry(cmdp);
6840 }
6841 }
6842 return 0;
6843 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006844
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006845 c = 0;
6846 while ((name = *argptr) != NULL) {
6847 cmdp = cmdlookup(name, 0);
6848 if (cmdp != NULL
6849 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006850 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6851 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006852 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006853 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006854 find_command(name, &entry, DO_ERR, pathval());
6855 if (entry.cmdtype == CMDUNKNOWN)
6856 c = 1;
6857 argptr++;
6858 }
6859 return c;
6860}
6861
6862/*
6863 * Called when a cd is done. Marks all commands so the next time they
6864 * are executed they will be rehashed.
6865 */
6866static void
6867hashcd(void)
6868{
6869 struct tblentry **pp;
6870 struct tblentry *cmdp;
6871
6872 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6873 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006874 if (cmdp->cmdtype == CMDNORMAL
6875 || (cmdp->cmdtype == CMDBUILTIN
6876 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6877 && builtinloc > 0)
6878 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006879 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006880 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006881 }
6882 }
6883}
6884
6885/*
6886 * Fix command hash table when PATH changed.
6887 * Called before PATH is changed. The argument is the new value of PATH;
6888 * pathval() still returns the old value at this point.
6889 * Called with interrupts off.
6890 */
6891static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006892changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006893{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006894 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006895 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006896 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006897 int idx_bltin;
6898
6899 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006900 firstchange = 9999; /* assume no change */
6901 idx = 0;
6902 idx_bltin = -1;
6903 for (;;) {
6904 if (*old != *new) {
6905 firstchange = idx;
6906 if ((*old == '\0' && *new == ':')
6907 || (*old == ':' && *new == '\0'))
6908 firstchange++;
6909 old = new; /* ignore subsequent differences */
6910 }
6911 if (*new == '\0')
6912 break;
6913 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6914 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006915 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006916 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006917 new++, old++;
6918 }
6919 if (builtinloc < 0 && idx_bltin >= 0)
6920 builtinloc = idx_bltin; /* zap builtins */
6921 if (builtinloc >= 0 && idx_bltin < 0)
6922 firstchange = 0;
6923 clearcmdentry(firstchange);
6924 builtinloc = idx_bltin;
6925}
6926
6927#define TEOF 0
6928#define TNL 1
6929#define TREDIR 2
6930#define TWORD 3
6931#define TSEMI 4
6932#define TBACKGND 5
6933#define TAND 6
6934#define TOR 7
6935#define TPIPE 8
6936#define TLP 9
6937#define TRP 10
6938#define TENDCASE 11
6939#define TENDBQUOTE 12
6940#define TNOT 13
6941#define TCASE 14
6942#define TDO 15
6943#define TDONE 16
6944#define TELIF 17
6945#define TELSE 18
6946#define TESAC 19
6947#define TFI 20
6948#define TFOR 21
6949#define TIF 22
6950#define TIN 23
6951#define TTHEN 24
6952#define TUNTIL 25
6953#define TWHILE 26
6954#define TBEGIN 27
6955#define TEND 28
6956
6957/* first char is indicating which tokens mark the end of a list */
6958static const char *const tokname_array[] = {
6959 "\1end of file",
6960 "\0newline",
6961 "\0redirection",
6962 "\0word",
6963 "\0;",
6964 "\0&",
6965 "\0&&",
6966 "\0||",
6967 "\0|",
6968 "\0(",
6969 "\1)",
6970 "\1;;",
6971 "\1`",
6972#define KWDOFFSET 13
6973 /* the following are keywords */
6974 "\0!",
6975 "\0case",
6976 "\1do",
6977 "\1done",
6978 "\1elif",
6979 "\1else",
6980 "\1esac",
6981 "\1fi",
6982 "\0for",
6983 "\0if",
6984 "\0in",
6985 "\1then",
6986 "\0until",
6987 "\0while",
6988 "\0{",
6989 "\1}",
6990};
6991
6992static const char *
6993tokname(int tok)
6994{
6995 static char buf[16];
6996
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00006997//try this:
6998//if (tok < TSEMI) return tokname_array[tok] + 1;
6999//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7000//return buf;
7001
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007002 if (tok >= TSEMI)
7003 buf[0] = '"';
7004 sprintf(buf + (tok >= TSEMI), "%s%c",
7005 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7006 return buf;
7007}
7008
7009/* Wrapper around strcmp for qsort/bsearch/... */
7010static int
7011pstrcmp(const void *a, const void *b)
7012{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007013 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007014}
7015
7016static const char *const *
7017findkwd(const char *s)
7018{
7019 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007020 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7021 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007022}
7023
7024/*
7025 * Locate and print what a word is...
7026 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007027static int
7028describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007029{
7030 struct cmdentry entry;
7031 struct tblentry *cmdp;
7032#if ENABLE_ASH_ALIAS
7033 const struct alias *ap;
7034#endif
7035 const char *path = pathval();
7036
7037 if (describe_command_verbose) {
7038 out1str(command);
7039 }
7040
7041 /* First look at the keywords */
7042 if (findkwd(command)) {
7043 out1str(describe_command_verbose ? " is a shell keyword" : command);
7044 goto out;
7045 }
7046
7047#if ENABLE_ASH_ALIAS
7048 /* Then look at the aliases */
7049 ap = lookupalias(command, 0);
7050 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007051 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007052 out1str("alias ");
7053 printalias(ap);
7054 return 0;
7055 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007056 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007057 goto out;
7058 }
7059#endif
7060 /* Then check if it is a tracked alias */
7061 cmdp = cmdlookup(command, 0);
7062 if (cmdp != NULL) {
7063 entry.cmdtype = cmdp->cmdtype;
7064 entry.u = cmdp->param;
7065 } else {
7066 /* Finally use brute force */
7067 find_command(command, &entry, DO_ABS, path);
7068 }
7069
7070 switch (entry.cmdtype) {
7071 case CMDNORMAL: {
7072 int j = entry.u.index;
7073 char *p;
7074 if (j == -1) {
7075 p = command;
7076 } else {
7077 do {
7078 p = padvance(&path, command);
7079 stunalloc(p);
7080 } while (--j >= 0);
7081 }
7082 if (describe_command_verbose) {
7083 out1fmt(" is%s %s",
7084 (cmdp ? " a tracked alias for" : nullstr), p
7085 );
7086 } else {
7087 out1str(p);
7088 }
7089 break;
7090 }
7091
7092 case CMDFUNCTION:
7093 if (describe_command_verbose) {
7094 out1str(" is a shell function");
7095 } else {
7096 out1str(command);
7097 }
7098 break;
7099
7100 case CMDBUILTIN:
7101 if (describe_command_verbose) {
7102 out1fmt(" is a %sshell builtin",
7103 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7104 "special " : nullstr
7105 );
7106 } else {
7107 out1str(command);
7108 }
7109 break;
7110
7111 default:
7112 if (describe_command_verbose) {
7113 out1str(": not found\n");
7114 }
7115 return 127;
7116 }
7117 out:
7118 outstr("\n", stdout);
7119 return 0;
7120}
7121
7122static int
7123typecmd(int argc, char **argv)
7124{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007125 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007126 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007127 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007128
Denis Vlasenko46846e22007-05-20 13:08:31 +00007129 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007130 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007131 i++;
7132 verbose = 0;
7133 }
7134 while (i < argc) {
7135 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007136 }
7137 return err;
7138}
7139
7140#if ENABLE_ASH_CMDCMD
7141static int
7142commandcmd(int argc, char **argv)
7143{
7144 int c;
7145 enum {
7146 VERIFY_BRIEF = 1,
7147 VERIFY_VERBOSE = 2,
7148 } verify = 0;
7149
7150 while ((c = nextopt("pvV")) != '\0')
7151 if (c == 'V')
7152 verify |= VERIFY_VERBOSE;
7153 else if (c == 'v')
7154 verify |= VERIFY_BRIEF;
7155#if DEBUG
7156 else if (c != 'p')
7157 abort();
7158#endif
7159 if (verify)
7160 return describe_command(*argptr, verify - VERIFY_BRIEF);
7161
7162 return 0;
7163}
7164#endif
7165
7166
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007167/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007168
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007169static int funcblocksize; /* size of structures in function */
7170static int funcstringsize; /* size of strings in node */
7171static void *funcblock; /* block to allocate function from */
7172static char *funcstring; /* block to allocate strings from */
7173
Eric Andersencb57d552001-06-28 07:25:16 +00007174/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007175#define EV_EXIT 01 /* exit after evaluating tree */
7176#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7177#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007178
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007179static const short nodesize[26] = {
7180 SHELL_ALIGN(sizeof(struct ncmd)),
7181 SHELL_ALIGN(sizeof(struct npipe)),
7182 SHELL_ALIGN(sizeof(struct nredir)),
7183 SHELL_ALIGN(sizeof(struct nredir)),
7184 SHELL_ALIGN(sizeof(struct nredir)),
7185 SHELL_ALIGN(sizeof(struct nbinary)),
7186 SHELL_ALIGN(sizeof(struct nbinary)),
7187 SHELL_ALIGN(sizeof(struct nbinary)),
7188 SHELL_ALIGN(sizeof(struct nif)),
7189 SHELL_ALIGN(sizeof(struct nbinary)),
7190 SHELL_ALIGN(sizeof(struct nbinary)),
7191 SHELL_ALIGN(sizeof(struct nfor)),
7192 SHELL_ALIGN(sizeof(struct ncase)),
7193 SHELL_ALIGN(sizeof(struct nclist)),
7194 SHELL_ALIGN(sizeof(struct narg)),
7195 SHELL_ALIGN(sizeof(struct narg)),
7196 SHELL_ALIGN(sizeof(struct nfile)),
7197 SHELL_ALIGN(sizeof(struct nfile)),
7198 SHELL_ALIGN(sizeof(struct nfile)),
7199 SHELL_ALIGN(sizeof(struct nfile)),
7200 SHELL_ALIGN(sizeof(struct nfile)),
7201 SHELL_ALIGN(sizeof(struct ndup)),
7202 SHELL_ALIGN(sizeof(struct ndup)),
7203 SHELL_ALIGN(sizeof(struct nhere)),
7204 SHELL_ALIGN(sizeof(struct nhere)),
7205 SHELL_ALIGN(sizeof(struct nnot)),
7206};
7207
7208static void calcsize(union node *n);
7209
7210static void
7211sizenodelist(struct nodelist *lp)
7212{
7213 while (lp) {
7214 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7215 calcsize(lp->n);
7216 lp = lp->next;
7217 }
7218}
7219
7220static void
7221calcsize(union node *n)
7222{
7223 if (n == NULL)
7224 return;
7225 funcblocksize += nodesize[n->type];
7226 switch (n->type) {
7227 case NCMD:
7228 calcsize(n->ncmd.redirect);
7229 calcsize(n->ncmd.args);
7230 calcsize(n->ncmd.assign);
7231 break;
7232 case NPIPE:
7233 sizenodelist(n->npipe.cmdlist);
7234 break;
7235 case NREDIR:
7236 case NBACKGND:
7237 case NSUBSHELL:
7238 calcsize(n->nredir.redirect);
7239 calcsize(n->nredir.n);
7240 break;
7241 case NAND:
7242 case NOR:
7243 case NSEMI:
7244 case NWHILE:
7245 case NUNTIL:
7246 calcsize(n->nbinary.ch2);
7247 calcsize(n->nbinary.ch1);
7248 break;
7249 case NIF:
7250 calcsize(n->nif.elsepart);
7251 calcsize(n->nif.ifpart);
7252 calcsize(n->nif.test);
7253 break;
7254 case NFOR:
7255 funcstringsize += strlen(n->nfor.var) + 1;
7256 calcsize(n->nfor.body);
7257 calcsize(n->nfor.args);
7258 break;
7259 case NCASE:
7260 calcsize(n->ncase.cases);
7261 calcsize(n->ncase.expr);
7262 break;
7263 case NCLIST:
7264 calcsize(n->nclist.body);
7265 calcsize(n->nclist.pattern);
7266 calcsize(n->nclist.next);
7267 break;
7268 case NDEFUN:
7269 case NARG:
7270 sizenodelist(n->narg.backquote);
7271 funcstringsize += strlen(n->narg.text) + 1;
7272 calcsize(n->narg.next);
7273 break;
7274 case NTO:
7275 case NCLOBBER:
7276 case NFROM:
7277 case NFROMTO:
7278 case NAPPEND:
7279 calcsize(n->nfile.fname);
7280 calcsize(n->nfile.next);
7281 break;
7282 case NTOFD:
7283 case NFROMFD:
7284 calcsize(n->ndup.vname);
7285 calcsize(n->ndup.next);
7286 break;
7287 case NHERE:
7288 case NXHERE:
7289 calcsize(n->nhere.doc);
7290 calcsize(n->nhere.next);
7291 break;
7292 case NNOT:
7293 calcsize(n->nnot.com);
7294 break;
7295 };
7296}
7297
7298static char *
7299nodeckstrdup(char *s)
7300{
7301 char *rtn = funcstring;
7302
7303 strcpy(funcstring, s);
7304 funcstring += strlen(s) + 1;
7305 return rtn;
7306}
7307
7308static union node *copynode(union node *);
7309
7310static struct nodelist *
7311copynodelist(struct nodelist *lp)
7312{
7313 struct nodelist *start;
7314 struct nodelist **lpp;
7315
7316 lpp = &start;
7317 while (lp) {
7318 *lpp = funcblock;
7319 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7320 (*lpp)->n = copynode(lp->n);
7321 lp = lp->next;
7322 lpp = &(*lpp)->next;
7323 }
7324 *lpp = NULL;
7325 return start;
7326}
7327
7328static union node *
7329copynode(union node *n)
7330{
7331 union node *new;
7332
7333 if (n == NULL)
7334 return NULL;
7335 new = funcblock;
7336 funcblock = (char *) funcblock + nodesize[n->type];
7337
7338 switch (n->type) {
7339 case NCMD:
7340 new->ncmd.redirect = copynode(n->ncmd.redirect);
7341 new->ncmd.args = copynode(n->ncmd.args);
7342 new->ncmd.assign = copynode(n->ncmd.assign);
7343 break;
7344 case NPIPE:
7345 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7346 new->npipe.backgnd = n->npipe.backgnd;
7347 break;
7348 case NREDIR:
7349 case NBACKGND:
7350 case NSUBSHELL:
7351 new->nredir.redirect = copynode(n->nredir.redirect);
7352 new->nredir.n = copynode(n->nredir.n);
7353 break;
7354 case NAND:
7355 case NOR:
7356 case NSEMI:
7357 case NWHILE:
7358 case NUNTIL:
7359 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7360 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7361 break;
7362 case NIF:
7363 new->nif.elsepart = copynode(n->nif.elsepart);
7364 new->nif.ifpart = copynode(n->nif.ifpart);
7365 new->nif.test = copynode(n->nif.test);
7366 break;
7367 case NFOR:
7368 new->nfor.var = nodeckstrdup(n->nfor.var);
7369 new->nfor.body = copynode(n->nfor.body);
7370 new->nfor.args = copynode(n->nfor.args);
7371 break;
7372 case NCASE:
7373 new->ncase.cases = copynode(n->ncase.cases);
7374 new->ncase.expr = copynode(n->ncase.expr);
7375 break;
7376 case NCLIST:
7377 new->nclist.body = copynode(n->nclist.body);
7378 new->nclist.pattern = copynode(n->nclist.pattern);
7379 new->nclist.next = copynode(n->nclist.next);
7380 break;
7381 case NDEFUN:
7382 case NARG:
7383 new->narg.backquote = copynodelist(n->narg.backquote);
7384 new->narg.text = nodeckstrdup(n->narg.text);
7385 new->narg.next = copynode(n->narg.next);
7386 break;
7387 case NTO:
7388 case NCLOBBER:
7389 case NFROM:
7390 case NFROMTO:
7391 case NAPPEND:
7392 new->nfile.fname = copynode(n->nfile.fname);
7393 new->nfile.fd = n->nfile.fd;
7394 new->nfile.next = copynode(n->nfile.next);
7395 break;
7396 case NTOFD:
7397 case NFROMFD:
7398 new->ndup.vname = copynode(n->ndup.vname);
7399 new->ndup.dupfd = n->ndup.dupfd;
7400 new->ndup.fd = n->ndup.fd;
7401 new->ndup.next = copynode(n->ndup.next);
7402 break;
7403 case NHERE:
7404 case NXHERE:
7405 new->nhere.doc = copynode(n->nhere.doc);
7406 new->nhere.fd = n->nhere.fd;
7407 new->nhere.next = copynode(n->nhere.next);
7408 break;
7409 case NNOT:
7410 new->nnot.com = copynode(n->nnot.com);
7411 break;
7412 };
7413 new->type = n->type;
7414 return new;
7415}
7416
7417/*
7418 * Make a copy of a parse tree.
7419 */
7420static struct funcnode *
7421copyfunc(union node *n)
7422{
7423 struct funcnode *f;
7424 size_t blocksize;
7425
7426 funcblocksize = offsetof(struct funcnode, n);
7427 funcstringsize = 0;
7428 calcsize(n);
7429 blocksize = funcblocksize;
7430 f = ckmalloc(blocksize + funcstringsize);
7431 funcblock = (char *) f + offsetof(struct funcnode, n);
7432 funcstring = (char *) f + blocksize;
7433 copynode(n);
7434 f->count = 0;
7435 return f;
7436}
7437
7438/*
7439 * Define a shell function.
7440 */
7441static void
7442defun(char *name, union node *func)
7443{
7444 struct cmdentry entry;
7445
7446 INT_OFF;
7447 entry.cmdtype = CMDFUNCTION;
7448 entry.u.func = copyfunc(func);
7449 addcmdentry(name, &entry);
7450 INT_ON;
7451}
7452
7453static int evalskip; /* set if we are skipping commands */
7454/* reasons for skipping commands (see comment on breakcmd routine) */
7455#define SKIPBREAK (1 << 0)
7456#define SKIPCONT (1 << 1)
7457#define SKIPFUNC (1 << 2)
7458#define SKIPFILE (1 << 3)
7459#define SKIPEVAL (1 << 4)
7460static int skipcount; /* number of levels to skip */
7461static int funcnest; /* depth of function calls */
7462
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007463/* forward decl way out to parsing code - dotrap needs it */
7464static int evalstring(char *s, int mask);
7465
7466/*
7467 * Called to execute a trap. Perhaps we should avoid entering new trap
7468 * handlers while we are executing a trap handler.
7469 */
7470static int
7471dotrap(void)
7472{
7473 char *p;
7474 char *q;
7475 int i;
7476 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007477 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007478
7479 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007480 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007481 xbarrier();
7482
7483 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7484 if (!*q)
7485 continue;
7486 *q = '\0';
7487
7488 p = trap[i + 1];
7489 if (!p)
7490 continue;
7491 skip = evalstring(p, SKIPEVAL);
7492 exitstatus = savestatus;
7493 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007494 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007495 }
7496
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007497 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007498}
7499
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007500/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007501static void evalloop(union node *, int);
7502static void evalfor(union node *, int);
7503static void evalcase(union node *, int);
7504static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007505static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007506static void evalpipe(union node *, int);
7507static void evalcommand(union node *, int);
7508static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007509static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007510
Eric Andersen62483552001-07-10 06:09:16 +00007511/*
Eric Andersenc470f442003-07-28 09:56:35 +00007512 * Evaluate a parse tree. The value is left in the global variable
7513 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007514 */
Eric Andersenc470f442003-07-28 09:56:35 +00007515static void
7516evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007517{
Eric Andersenc470f442003-07-28 09:56:35 +00007518 int checkexit = 0;
7519 void (*evalfn)(union node *, int);
7520 unsigned isor;
7521 int status;
7522 if (n == NULL) {
7523 TRACE(("evaltree(NULL) called\n"));
7524 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007525 }
Eric Andersenc470f442003-07-28 09:56:35 +00007526 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007527 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007528 switch (n->type) {
7529 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007530#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007531 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007532 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007533 break;
7534#endif
7535 case NNOT:
7536 evaltree(n->nnot.com, EV_TESTED);
7537 status = !exitstatus;
7538 goto setstatus;
7539 case NREDIR:
7540 expredir(n->nredir.redirect);
7541 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7542 if (!status) {
7543 evaltree(n->nredir.n, flags & EV_TESTED);
7544 status = exitstatus;
7545 }
7546 popredir(0);
7547 goto setstatus;
7548 case NCMD:
7549 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007550 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007551 if (eflag && !(flags & EV_TESTED))
7552 checkexit = ~0;
7553 goto calleval;
7554 case NFOR:
7555 evalfn = evalfor;
7556 goto calleval;
7557 case NWHILE:
7558 case NUNTIL:
7559 evalfn = evalloop;
7560 goto calleval;
7561 case NSUBSHELL:
7562 case NBACKGND:
7563 evalfn = evalsubshell;
7564 goto calleval;
7565 case NPIPE:
7566 evalfn = evalpipe;
7567 goto checkexit;
7568 case NCASE:
7569 evalfn = evalcase;
7570 goto calleval;
7571 case NAND:
7572 case NOR:
7573 case NSEMI:
7574#if NAND + 1 != NOR
7575#error NAND + 1 != NOR
7576#endif
7577#if NOR + 1 != NSEMI
7578#error NOR + 1 != NSEMI
7579#endif
7580 isor = n->type - NAND;
7581 evaltree(
7582 n->nbinary.ch1,
7583 (flags | ((isor >> 1) - 1)) & EV_TESTED
7584 );
7585 if (!exitstatus == isor)
7586 break;
7587 if (!evalskip) {
7588 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007589 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007590 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007591 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007592 evalfn(n, flags);
7593 break;
7594 }
7595 break;
7596 case NIF:
7597 evaltree(n->nif.test, EV_TESTED);
7598 if (evalskip)
7599 break;
7600 if (exitstatus == 0) {
7601 n = n->nif.ifpart;
7602 goto evaln;
7603 } else if (n->nif.elsepart) {
7604 n = n->nif.elsepart;
7605 goto evaln;
7606 }
7607 goto success;
7608 case NDEFUN:
7609 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007610 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007611 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007612 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007613 exitstatus = status;
7614 break;
7615 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007616 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007617 if ((checkexit & exitstatus))
7618 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007619 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007620 goto exexit;
7621
7622 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007623 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007624 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007625 }
Eric Andersen62483552001-07-10 06:09:16 +00007626}
7627
Eric Andersenc470f442003-07-28 09:56:35 +00007628#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7629static
7630#endif
7631void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7632
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007633static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007634
7635static void
7636evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007637{
7638 int status;
7639
7640 loopnest++;
7641 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007642 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007643 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007644 int i;
7645
Eric Andersencb57d552001-06-28 07:25:16 +00007646 evaltree(n->nbinary.ch1, EV_TESTED);
7647 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007648 skipping:
7649 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007650 evalskip = 0;
7651 continue;
7652 }
7653 if (evalskip == SKIPBREAK && --skipcount <= 0)
7654 evalskip = 0;
7655 break;
7656 }
Eric Andersenc470f442003-07-28 09:56:35 +00007657 i = exitstatus;
7658 if (n->type != NWHILE)
7659 i = !i;
7660 if (i != 0)
7661 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007662 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007663 status = exitstatus;
7664 if (evalskip)
7665 goto skipping;
7666 }
7667 loopnest--;
7668 exitstatus = status;
7669}
7670
Eric Andersenc470f442003-07-28 09:56:35 +00007671static void
7672evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007673{
7674 struct arglist arglist;
7675 union node *argp;
7676 struct strlist *sp;
7677 struct stackmark smark;
7678
7679 setstackmark(&smark);
7680 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007681 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007682 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007683 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007684 if (evalskip)
7685 goto out;
7686 }
7687 *arglist.lastp = NULL;
7688
7689 exitstatus = 0;
7690 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007691 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007692 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007693 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007694 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007695 if (evalskip) {
7696 if (evalskip == SKIPCONT && --skipcount <= 0) {
7697 evalskip = 0;
7698 continue;
7699 }
7700 if (evalskip == SKIPBREAK && --skipcount <= 0)
7701 evalskip = 0;
7702 break;
7703 }
7704 }
7705 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007706 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007707 popstackmark(&smark);
7708}
7709
Eric Andersenc470f442003-07-28 09:56:35 +00007710static void
7711evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007712{
7713 union node *cp;
7714 union node *patp;
7715 struct arglist arglist;
7716 struct stackmark smark;
7717
7718 setstackmark(&smark);
7719 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007720 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007721 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007722 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7723 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007724 if (casematch(patp, arglist.list->text)) {
7725 if (evalskip == 0) {
7726 evaltree(cp->nclist.body, flags);
7727 }
7728 goto out;
7729 }
7730 }
7731 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007732 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007733 popstackmark(&smark);
7734}
7735
Eric Andersenc470f442003-07-28 09:56:35 +00007736/*
7737 * Kick off a subshell to evaluate a tree.
7738 */
Eric Andersenc470f442003-07-28 09:56:35 +00007739static void
7740evalsubshell(union node *n, int flags)
7741{
7742 struct job *jp;
7743 int backgnd = (n->type == NBACKGND);
7744 int status;
7745
7746 expredir(n->nredir.redirect);
7747 if (!backgnd && flags & EV_EXIT && !trap[0])
7748 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007749 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007750 jp = makejob(n, 1);
7751 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007752 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007753 flags |= EV_EXIT;
7754 if (backgnd)
7755 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007756 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007757 redirect(n->nredir.redirect, 0);
7758 evaltreenr(n->nredir.n, flags);
7759 /* never returns */
7760 }
7761 status = 0;
7762 if (! backgnd)
7763 status = waitforjob(jp);
7764 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007765 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007766}
7767
Eric Andersenc470f442003-07-28 09:56:35 +00007768/*
7769 * Compute the names of the files in a redirection list.
7770 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007771static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007772static void
7773expredir(union node *n)
7774{
7775 union node *redir;
7776
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007777 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007778 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007779
7780 memset(&fn, 0, sizeof(fn));
Eric Andersenc470f442003-07-28 09:56:35 +00007781 fn.lastp = &fn.list;
7782 switch (redir->type) {
7783 case NFROMTO:
7784 case NFROM:
7785 case NTO:
7786 case NCLOBBER:
7787 case NAPPEND:
7788 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7789 redir->nfile.expfname = fn.list->text;
7790 break;
7791 case NFROMFD:
7792 case NTOFD:
7793 if (redir->ndup.vname) {
7794 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007795 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007796 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007797 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007798 }
7799 break;
7800 }
7801 }
7802}
7803
Eric Andersencb57d552001-06-28 07:25:16 +00007804/*
Eric Andersencb57d552001-06-28 07:25:16 +00007805 * Evaluate a pipeline. All the processes in the pipeline are children
7806 * of the process creating the pipeline. (This differs from some versions
7807 * of the shell, which make the last process in a pipeline the parent
7808 * of all the rest.)
7809 */
Eric Andersenc470f442003-07-28 09:56:35 +00007810static void
7811evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007812{
7813 struct job *jp;
7814 struct nodelist *lp;
7815 int pipelen;
7816 int prevfd;
7817 int pip[2];
7818
Eric Andersenc470f442003-07-28 09:56:35 +00007819 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007820 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007821 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007822 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007823 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007824 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007825 jp = makejob(n, pipelen);
7826 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007827 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007828 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007829 pip[1] = -1;
7830 if (lp->next) {
7831 if (pipe(pip) < 0) {
7832 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007833 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007834 }
7835 }
7836 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007837 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007838 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007839 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007840 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007841 if (prevfd > 0) {
7842 dup2(prevfd, 0);
7843 close(prevfd);
7844 }
7845 if (pip[1] > 1) {
7846 dup2(pip[1], 1);
7847 close(pip[1]);
7848 }
Eric Andersenc470f442003-07-28 09:56:35 +00007849 evaltreenr(lp->n, flags);
7850 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007851 }
7852 if (prevfd >= 0)
7853 close(prevfd);
7854 prevfd = pip[0];
7855 close(pip[1]);
7856 }
Eric Andersencb57d552001-06-28 07:25:16 +00007857 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007858 exitstatus = waitforjob(jp);
7859 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007860 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007861 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007862}
7863
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007864/*
7865 * Controls whether the shell is interactive or not.
7866 */
7867static void
7868setinteractive(int on)
7869{
7870 static int is_interactive;
7871
7872 if (++on == is_interactive)
7873 return;
7874 is_interactive = on;
7875 setsignal(SIGINT);
7876 setsignal(SIGQUIT);
7877 setsignal(SIGTERM);
7878#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7879 if (is_interactive > 1) {
7880 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007881 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007882
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007883 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007884 out1fmt(
7885 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007886 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007887 "Enter 'help' for a list of built-in commands."
7888 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007889 bb_banner);
7890 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007891 }
7892 }
7893#endif
7894}
7895
7896#if ENABLE_FEATURE_EDITING_VI
7897#define setvimode(on) do { \
7898 if (on) line_input_state->flags |= VI_MODE; \
7899 else line_input_state->flags &= ~VI_MODE; \
7900} while (0)
7901#else
7902#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7903#endif
7904
7905static void
7906optschanged(void)
7907{
7908#if DEBUG
7909 opentrace();
7910#endif
7911 setinteractive(iflag);
7912 setjobctl(mflag);
7913 setvimode(viflag);
7914}
7915
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007916static struct localvar *localvars;
7917
7918/*
7919 * Called after a function returns.
7920 * Interrupts must be off.
7921 */
7922static void
7923poplocalvars(void)
7924{
7925 struct localvar *lvp;
7926 struct var *vp;
7927
7928 while ((lvp = localvars) != NULL) {
7929 localvars = lvp->next;
7930 vp = lvp->vp;
7931 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7932 if (vp == NULL) { /* $- saved */
7933 memcpy(optlist, lvp->text, sizeof(optlist));
7934 free((char*)lvp->text);
7935 optschanged();
7936 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7937 unsetvar(vp->text);
7938 } else {
7939 if (vp->func)
7940 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7941 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7942 free((char*)vp->text);
7943 vp->flags = lvp->flags;
7944 vp->text = lvp->text;
7945 }
7946 free(lvp);
7947 }
7948}
7949
7950static int
7951evalfun(struct funcnode *func, int argc, char **argv, int flags)
7952{
7953 volatile struct shparam saveparam;
7954 struct localvar *volatile savelocalvars;
7955 struct jmploc *volatile savehandler;
7956 struct jmploc jmploc;
7957 int e;
7958
7959 saveparam = shellparam;
7960 savelocalvars = localvars;
7961 e = setjmp(jmploc.loc);
7962 if (e) {
7963 goto funcdone;
7964 }
7965 INT_OFF;
7966 savehandler = exception_handler;
7967 exception_handler = &jmploc;
7968 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007969 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007970 func->count++;
7971 funcnest++;
7972 INT_ON;
7973 shellparam.nparam = argc - 1;
7974 shellparam.p = argv + 1;
7975#if ENABLE_ASH_GETOPTS
7976 shellparam.optind = 1;
7977 shellparam.optoff = -1;
7978#endif
7979 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007980 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007981 INT_OFF;
7982 funcnest--;
7983 freefunc(func);
7984 poplocalvars();
7985 localvars = savelocalvars;
7986 freeparam(&shellparam);
7987 shellparam = saveparam;
7988 exception_handler = savehandler;
7989 INT_ON;
7990 evalskip &= ~SKIPFUNC;
7991 return e;
7992}
7993
Denis Vlasenko131ae172007-02-18 13:00:19 +00007994#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00007995static char **
7996parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00007997{
7998 char *cp, c;
7999
8000 for (;;) {
8001 cp = *++argv;
8002 if (!cp)
8003 return 0;
8004 if (*cp++ != '-')
8005 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008006 c = *cp++;
8007 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008008 break;
8009 if (c == '-' && !*cp) {
8010 argv++;
8011 break;
8012 }
8013 do {
8014 switch (c) {
8015 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008016 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008017 break;
8018 default:
8019 /* run 'typecmd' for other options */
8020 return 0;
8021 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008022 c = *cp++;
8023 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008024 }
8025 return argv;
8026}
8027#endif
8028
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008029/*
8030 * Make a variable a local variable. When a variable is made local, it's
8031 * value and flags are saved in a localvar structure. The saved values
8032 * will be restored when the shell function returns. We handle the name
8033 * "-" as a special case.
8034 */
8035static void
8036mklocal(char *name)
8037{
8038 struct localvar *lvp;
8039 struct var **vpp;
8040 struct var *vp;
8041
8042 INT_OFF;
8043 lvp = ckmalloc(sizeof(struct localvar));
8044 if (LONE_DASH(name)) {
8045 char *p;
8046 p = ckmalloc(sizeof(optlist));
8047 lvp->text = memcpy(p, optlist, sizeof(optlist));
8048 vp = NULL;
8049 } else {
8050 char *eq;
8051
8052 vpp = hashvar(name);
8053 vp = *findvar(vpp, name);
8054 eq = strchr(name, '=');
8055 if (vp == NULL) {
8056 if (eq)
8057 setvareq(name, VSTRFIXED);
8058 else
8059 setvar(name, NULL, VSTRFIXED);
8060 vp = *vpp; /* the new variable */
8061 lvp->flags = VUNSET;
8062 } else {
8063 lvp->text = vp->text;
8064 lvp->flags = vp->flags;
8065 vp->flags |= VSTRFIXED|VTEXTFIXED;
8066 if (eq)
8067 setvareq(name, 0);
8068 }
8069 }
8070 lvp->vp = vp;
8071 lvp->next = localvars;
8072 localvars = lvp;
8073 INT_ON;
8074}
8075
8076/*
8077 * The "local" command.
8078 */
8079static int
8080localcmd(int argc, char **argv)
8081{
8082 char *name;
8083
8084 argv = argptr;
8085 while ((name = *argv++) != NULL) {
8086 mklocal(name);
8087 }
8088 return 0;
8089}
8090
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008091static int
8092falsecmd(int argc, char **argv)
8093{
8094 return 1;
8095}
8096
8097static int
8098truecmd(int argc, char **argv)
8099{
8100 return 0;
8101}
8102
8103static int
8104execcmd(int argc, char **argv)
8105{
8106 if (argc > 1) {
8107 iflag = 0; /* exit on error */
8108 mflag = 0;
8109 optschanged();
8110 shellexec(argv + 1, pathval(), 0);
8111 }
8112 return 0;
8113}
8114
8115/*
8116 * The return command.
8117 */
8118static int
8119returncmd(int argc, char **argv)
8120{
8121 /*
8122 * If called outside a function, do what ksh does;
8123 * skip the rest of the file.
8124 */
8125 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8126 return argv[1] ? number(argv[1]) : exitstatus;
8127}
8128
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008129/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008130static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008131static int dotcmd(int, char **);
8132static int evalcmd(int, char **);
8133#if ENABLE_ASH_BUILTIN_ECHO
8134static int echocmd(int, char **);
8135#endif
8136#if ENABLE_ASH_BUILTIN_TEST
8137static int testcmd(int, char **);
8138#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008139static int exitcmd(int, char **);
8140static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008141#if ENABLE_ASH_GETOPTS
8142static int getoptscmd(int, char **);
8143#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008144#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8145static int helpcmd(int argc, char **argv);
8146#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008147#if ENABLE_ASH_MATH_SUPPORT
8148static int letcmd(int, char **);
8149#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008150static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008151static int setcmd(int, char **);
8152static int shiftcmd(int, char **);
8153static int timescmd(int, char **);
8154static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008155static int umaskcmd(int, char **);
8156static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008157static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008158
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008159#define BUILTIN_NOSPEC "0"
8160#define BUILTIN_SPECIAL "1"
8161#define BUILTIN_REGULAR "2"
8162#define BUILTIN_SPEC_REG "3"
8163#define BUILTIN_ASSIGN "4"
8164#define BUILTIN_SPEC_ASSG "5"
8165#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008166#define BUILTIN_SPEC_REG_ASSG "7"
8167
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008168/* make sure to keep these in proper order since it is searched via bsearch() */
8169static const struct builtincmd builtintab[] = {
8170 { BUILTIN_SPEC_REG ".", dotcmd },
8171 { BUILTIN_SPEC_REG ":", truecmd },
8172#if ENABLE_ASH_BUILTIN_TEST
8173 { BUILTIN_REGULAR "[", testcmd },
8174 { BUILTIN_REGULAR "[[", testcmd },
8175#endif
8176#if ENABLE_ASH_ALIAS
8177 { BUILTIN_REG_ASSG "alias", aliascmd },
8178#endif
8179#if JOBS
8180 { BUILTIN_REGULAR "bg", fg_bgcmd },
8181#endif
8182 { BUILTIN_SPEC_REG "break", breakcmd },
8183 { BUILTIN_REGULAR "cd", cdcmd },
8184 { BUILTIN_NOSPEC "chdir", cdcmd },
8185#if ENABLE_ASH_CMDCMD
8186 { BUILTIN_REGULAR "command", commandcmd },
8187#endif
8188 { BUILTIN_SPEC_REG "continue", breakcmd },
8189#if ENABLE_ASH_BUILTIN_ECHO
8190 { BUILTIN_REGULAR "echo", echocmd },
8191#endif
8192 { BUILTIN_SPEC_REG "eval", evalcmd },
8193 { BUILTIN_SPEC_REG "exec", execcmd },
8194 { BUILTIN_SPEC_REG "exit", exitcmd },
8195 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8196 { BUILTIN_REGULAR "false", falsecmd },
8197#if JOBS
8198 { BUILTIN_REGULAR "fg", fg_bgcmd },
8199#endif
8200#if ENABLE_ASH_GETOPTS
8201 { BUILTIN_REGULAR "getopts", getoptscmd },
8202#endif
8203 { BUILTIN_NOSPEC "hash", hashcmd },
8204#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8205 { BUILTIN_NOSPEC "help", helpcmd },
8206#endif
8207#if JOBS
8208 { BUILTIN_REGULAR "jobs", jobscmd },
8209 { BUILTIN_REGULAR "kill", killcmd },
8210#endif
8211#if ENABLE_ASH_MATH_SUPPORT
8212 { BUILTIN_NOSPEC "let", letcmd },
8213#endif
8214 { BUILTIN_ASSIGN "local", localcmd },
8215 { BUILTIN_NOSPEC "pwd", pwdcmd },
8216 { BUILTIN_REGULAR "read", readcmd },
8217 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8218 { BUILTIN_SPEC_REG "return", returncmd },
8219 { BUILTIN_SPEC_REG "set", setcmd },
8220 { BUILTIN_SPEC_REG "shift", shiftcmd },
8221 { BUILTIN_SPEC_REG "source", dotcmd },
8222#if ENABLE_ASH_BUILTIN_TEST
8223 { BUILTIN_REGULAR "test", testcmd },
8224#endif
8225 { BUILTIN_SPEC_REG "times", timescmd },
8226 { BUILTIN_SPEC_REG "trap", trapcmd },
8227 { BUILTIN_REGULAR "true", truecmd },
8228 { BUILTIN_NOSPEC "type", typecmd },
8229 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8230 { BUILTIN_REGULAR "umask", umaskcmd },
8231#if ENABLE_ASH_ALIAS
8232 { BUILTIN_REGULAR "unalias", unaliascmd },
8233#endif
8234 { BUILTIN_SPEC_REG "unset", unsetcmd },
8235 { BUILTIN_REGULAR "wait", waitcmd },
8236};
8237
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008238
8239#define COMMANDCMD (builtintab + 5 + \
8240 2 * ENABLE_ASH_BUILTIN_TEST + \
8241 ENABLE_ASH_ALIAS + \
8242 ENABLE_ASH_JOB_CONTROL)
8243#define EXECCMD (builtintab + 7 + \
8244 2 * ENABLE_ASH_BUILTIN_TEST + \
8245 ENABLE_ASH_ALIAS + \
8246 ENABLE_ASH_JOB_CONTROL + \
8247 ENABLE_ASH_CMDCMD + \
8248 ENABLE_ASH_BUILTIN_ECHO)
8249
8250/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008251 * Search the table of builtin commands.
8252 */
8253static struct builtincmd *
8254find_builtin(const char *name)
8255{
8256 struct builtincmd *bp;
8257
8258 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008259 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008260 pstrcmp
8261 );
8262 return bp;
8263}
8264
8265/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008266 * Execute a simple command.
8267 */
8268static int back_exitstatus; /* exit status of backquoted command */
8269static int
8270isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008271{
8272 const char *q = endofname(p);
8273 if (p == q)
8274 return 0;
8275 return *q == '=';
8276}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008277static int
8278bltincmd(int argc, char **argv)
8279{
8280 /* Preserve exitstatus of a previous possible redirection
8281 * as POSIX mandates */
8282 return back_exitstatus;
8283}
Eric Andersenc470f442003-07-28 09:56:35 +00008284static void
8285evalcommand(union node *cmd, int flags)
8286{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008287 static const struct builtincmd null_bltin = {
8288 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008289 };
Eric Andersenc470f442003-07-28 09:56:35 +00008290 struct stackmark smark;
8291 union node *argp;
8292 struct arglist arglist;
8293 struct arglist varlist;
8294 char **argv;
8295 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008296 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008297 struct cmdentry cmdentry;
8298 struct job *jp;
8299 char *lastarg;
8300 const char *path;
8301 int spclbltin;
8302 int cmd_is_exec;
8303 int status;
8304 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008305 struct builtincmd *bcmd;
8306 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008307
8308 /* First expand the arguments. */
8309 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8310 setstackmark(&smark);
8311 back_exitstatus = 0;
8312
8313 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008314 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008315 varlist.lastp = &varlist.list;
8316 *varlist.lastp = NULL;
8317 arglist.lastp = &arglist.list;
8318 *arglist.lastp = NULL;
8319
8320 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008321 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008322 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8323 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8324 }
8325
Eric Andersenc470f442003-07-28 09:56:35 +00008326 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8327 struct strlist **spp;
8328
8329 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008330 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008331 expandarg(argp, &arglist, EXP_VARTILDE);
8332 else
8333 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8334
Eric Andersenc470f442003-07-28 09:56:35 +00008335 for (sp = *spp; sp; sp = sp->next)
8336 argc++;
8337 }
8338
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008339 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008340 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008341 TRACE(("evalcommand arg: %s\n", sp->text));
8342 *nargv++ = sp->text;
8343 }
8344 *nargv = NULL;
8345
8346 lastarg = NULL;
8347 if (iflag && funcnest == 0 && argc > 0)
8348 lastarg = nargv[-1];
8349
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008350 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008351 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008352 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008353
8354 path = vpath.text;
8355 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8356 struct strlist **spp;
8357 char *p;
8358
8359 spp = varlist.lastp;
8360 expandarg(argp, &varlist, EXP_VARTILDE);
8361
8362 /*
8363 * Modify the command lookup path, if a PATH= assignment
8364 * is present
8365 */
8366 p = (*spp)->text;
8367 if (varequal(p, path))
8368 path = p;
8369 }
8370
8371 /* Print the command if xflag is set. */
8372 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008373 int n;
8374 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008375
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008376 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008377 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008378
8379 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008380 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008381 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008382 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008383 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008384 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008385 p--;
8386 }
8387 }
8388 sp = arglist.list;
8389 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008390 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008391 }
8392
8393 cmd_is_exec = 0;
8394 spclbltin = -1;
8395
8396 /* Now locate the command. */
8397 if (argc) {
8398 const char *oldpath;
8399 int cmd_flag = DO_ERR;
8400
8401 path += 5;
8402 oldpath = path;
8403 for (;;) {
8404 find_command(argv[0], &cmdentry, cmd_flag, path);
8405 if (cmdentry.cmdtype == CMDUNKNOWN) {
8406 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008407 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008408 goto bail;
8409 }
8410
8411 /* implement bltin and command here */
8412 if (cmdentry.cmdtype != CMDBUILTIN)
8413 break;
8414 if (spclbltin < 0)
8415 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8416 if (cmdentry.u.cmd == EXECCMD)
8417 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008418#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008419 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008420 path = oldpath;
8421 nargv = parse_command_args(argv, &path);
8422 if (!nargv)
8423 break;
8424 argc -= nargv - argv;
8425 argv = nargv;
8426 cmd_flag |= DO_NOFUNC;
8427 } else
8428#endif
8429 break;
8430 }
8431 }
8432
8433 if (status) {
8434 /* We have a redirection error. */
8435 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008436 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008437 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008438 exitstatus = status;
8439 goto out;
8440 }
8441
8442 /* Execute the command. */
8443 switch (cmdentry.cmdtype) {
8444 default:
8445 /* Fork off a child process if necessary. */
8446 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008447 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008448 jp = makejob(cmd, 1);
8449 if (forkshell(jp, cmd, FORK_FG) != 0) {
8450 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008451 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008452 break;
8453 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008454 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008455 }
8456 listsetvar(varlist.list, VEXPORT|VSTACK);
8457 shellexec(argv, path, cmdentry.u.index);
8458 /* NOTREACHED */
8459
8460 case CMDBUILTIN:
8461 cmdenviron = varlist.list;
8462 if (cmdenviron) {
8463 struct strlist *list = cmdenviron;
8464 int i = VNOSET;
8465 if (spclbltin > 0 || argc == 0) {
8466 i = 0;
8467 if (cmd_is_exec && argc > 1)
8468 i = VEXPORT;
8469 }
8470 listsetvar(list, i);
8471 }
8472 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8473 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008474 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008475 if (i == EXEXIT)
8476 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008477 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008478 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008479 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008480 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008481 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008482 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008483 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008484 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008485 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008486 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008487 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008488 }
8489 break;
8490
8491 case CMDFUNCTION:
8492 listsetvar(varlist.list, 0);
8493 if (evalfun(cmdentry.u.func, argc, argv, flags))
8494 goto raise;
8495 break;
8496 }
8497
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008498 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008499 popredir(cmd_is_exec);
8500 if (lastarg)
8501 /* dsl: I think this is intended to be used to support
8502 * '_' in 'vi' command mode during line editing...
8503 * However I implemented that within libedit itself.
8504 */
8505 setvar("_", lastarg, 0);
8506 popstackmark(&smark);
8507}
8508
8509static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008510evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8511{
Eric Andersenc470f442003-07-28 09:56:35 +00008512 char *volatile savecmdname;
8513 struct jmploc *volatile savehandler;
8514 struct jmploc jmploc;
8515 int i;
8516
8517 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008518 i = setjmp(jmploc.loc);
8519 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008520 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008521 savehandler = exception_handler;
8522 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008523 commandname = argv[0];
8524 argptr = argv + 1;
8525 optptr = NULL; /* initialize nextopt */
8526 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008527 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008528 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008529 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008530 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008531 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008532// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008533 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008534
8535 return i;
8536}
8537
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008538static int
8539goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008540{
8541 return !*endofname(p);
8542}
8543
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008544
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008545/*
8546 * Search for a command. This is called before we fork so that the
8547 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008548 * the child. The check for "goodname" is an overly conservative
8549 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008550 */
Eric Andersenc470f442003-07-28 09:56:35 +00008551static void
8552prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008553{
8554 struct cmdentry entry;
8555
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008556 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8557 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008558}
8559
Eric Andersencb57d552001-06-28 07:25:16 +00008560
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008561/* ============ Builtin commands
8562 *
8563 * Builtin commands whose functions are closely tied to evaluation
8564 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008565 */
8566
8567/*
Eric Andersencb57d552001-06-28 07:25:16 +00008568 * Handle break and continue commands. Break, continue, and return are
8569 * all handled by setting the evalskip flag. The evaluation routines
8570 * above all check this flag, and if it is set they start skipping
8571 * commands rather than executing them. The variable skipcount is
8572 * the number of loops to break/continue, or the number of function
8573 * levels to return. (The latter is always 1.) It should probably
8574 * be an error to break out of more loops than exist, but it isn't
8575 * in the standard shell so we don't make it one here.
8576 */
Eric Andersenc470f442003-07-28 09:56:35 +00008577static int
8578breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008579{
8580 int n = argc > 1 ? number(argv[1]) : 1;
8581
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008582 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008583 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008584 if (n > loopnest)
8585 n = loopnest;
8586 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008587 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008588 skipcount = n;
8589 }
8590 return 0;
8591}
8592
Eric Andersenc470f442003-07-28 09:56:35 +00008593
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008594/* ============ input.c
8595 *
Eric Andersen90898442003-08-06 11:20:52 +00008596 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008597 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008598
Eric Andersenc470f442003-07-28 09:56:35 +00008599#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008600
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008601enum {
8602 INPUT_PUSH_FILE = 1,
8603 INPUT_NOFILE_OK = 2,
8604};
Eric Andersencb57d552001-06-28 07:25:16 +00008605
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008606static int plinno = 1; /* input line number */
8607/* number of characters left in input buffer */
8608static int parsenleft; /* copy of parsefile->nleft */
8609static int parselleft; /* copy of parsefile->lleft */
8610/* next character in input buffer */
8611static char *parsenextc; /* copy of parsefile->nextc */
8612
8613static int checkkwd;
8614/* values of checkkwd variable */
8615#define CHKALIAS 0x1
8616#define CHKKWD 0x2
8617#define CHKNL 0x4
8618
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008619static void
8620popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008621{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008622 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008623
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008624 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008625#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008626 if (sp->ap) {
8627 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8628 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008629 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008630 if (sp->string != sp->ap->val) {
8631 free(sp->string);
8632 }
8633 sp->ap->flag &= ~ALIASINUSE;
8634 if (sp->ap->flag & ALIASDEAD) {
8635 unalias(sp->ap->name);
8636 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008637 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008638#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008639 parsenextc = sp->prevstring;
8640 parsenleft = sp->prevnleft;
8641/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8642 parsefile->strpush = sp->prev;
8643 if (sp != &(parsefile->basestrpush))
8644 free(sp);
8645 INT_ON;
8646}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008647
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008648static int
8649preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008650{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008651 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008652 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008653 parsenextc = buf;
8654
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008655 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008656#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008657 if (!iflag || parsefile->fd)
8658 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8659 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008660#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008661 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008662#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008663 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8664 if (nr == 0) {
8665 /* Ctrl+C pressed */
8666 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008667 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008668 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008669 raise(SIGINT);
8670 return 1;
8671 }
Eric Andersenc470f442003-07-28 09:56:35 +00008672 goto retry;
8673 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008674 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008675 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008676 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008677 }
Eric Andersencb57d552001-06-28 07:25:16 +00008678 }
8679#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008680 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008681#endif
8682
8683 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008684 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008685 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008686 if (flags >= 0 && (flags & O_NONBLOCK)) {
8687 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008688 if (fcntl(0, F_SETFL, flags) >= 0) {
8689 out2str("sh: turning off NDELAY mode\n");
8690 goto retry;
8691 }
8692 }
8693 }
8694 }
8695 return nr;
8696}
8697
8698/*
8699 * Refill the input buffer and return the next input character:
8700 *
8701 * 1) If a string was pushed back on the input, pop it;
8702 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8703 * from a string so we can't refill the buffer, return EOF.
8704 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8705 * 4) Process input up to the next newline, deleting nul characters.
8706 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008707static int
Eric Andersenc470f442003-07-28 09:56:35 +00008708preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008709{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008710 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008711 int more;
8712 char savec;
8713
8714 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008715#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008716 if (parsenleft == -1 && parsefile->strpush->ap &&
8717 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008718 return PEOA;
8719 }
Eric Andersen2870d962001-07-02 17:27:21 +00008720#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008721 popstring();
8722 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008723 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008724 }
8725 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8726 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008727 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008728
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008729 more = parselleft;
8730 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008731 again:
8732 more = preadfd();
8733 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008734 parselleft = parsenleft = EOF_NLEFT;
8735 return PEOF;
8736 }
8737 }
8738
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008739 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008740
8741 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008742 for (;;) {
8743 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008744
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008745 more--;
8746 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008747
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008748 if (!c)
8749 memmove(q, q + 1, more);
8750 else {
8751 q++;
8752 if (c == '\n') {
8753 parsenleft = q - parsenextc - 1;
8754 break;
8755 }
Eric Andersencb57d552001-06-28 07:25:16 +00008756 }
8757
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008758 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008759 parsenleft = q - parsenextc - 1;
8760 if (parsenleft < 0)
8761 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008762 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008763 }
8764 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008765 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008766
8767 savec = *q;
8768 *q = '\0';
8769
8770 if (vflag) {
8771 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008772 }
8773
8774 *q = savec;
8775
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008776 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008777}
8778
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008779#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008780static int
8781pgetc(void)
8782{
8783 return pgetc_as_macro();
8784}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008785
8786#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8787#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008788#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008789#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008790#endif
8791
8792/*
8793 * Same as pgetc(), but ignores PEOA.
8794 */
8795#if ENABLE_ASH_ALIAS
8796static int
8797pgetc2(void)
8798{
8799 int c;
8800
8801 do {
8802 c = pgetc_macro();
8803 } while (c == PEOA);
8804 return c;
8805}
8806#else
8807static int
8808pgetc2(void)
8809{
8810 return pgetc_macro();
8811}
8812#endif
8813
8814/*
8815 * Read a line from the script.
8816 */
8817static char *
8818pfgets(char *line, int len)
8819{
8820 char *p = line;
8821 int nleft = len;
8822 int c;
8823
8824 while (--nleft > 0) {
8825 c = pgetc2();
8826 if (c == PEOF) {
8827 if (p == line)
8828 return NULL;
8829 break;
8830 }
8831 *p++ = c;
8832 if (c == '\n')
8833 break;
8834 }
8835 *p = '\0';
8836 return line;
8837}
8838
Eric Andersenc470f442003-07-28 09:56:35 +00008839/*
8840 * Undo the last call to pgetc. Only one character may be pushed back.
8841 * PEOF may be pushed back.
8842 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008843static void
Eric Andersenc470f442003-07-28 09:56:35 +00008844pungetc(void)
8845{
8846 parsenleft++;
8847 parsenextc--;
8848}
Eric Andersencb57d552001-06-28 07:25:16 +00008849
8850/*
8851 * Push a string back onto the input at this current parsefile level.
8852 * We handle aliases this way.
8853 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008854static void
Eric Andersenc470f442003-07-28 09:56:35 +00008855pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008856{
Eric Andersencb57d552001-06-28 07:25:16 +00008857 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008858 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008859
Eric Andersenc470f442003-07-28 09:56:35 +00008860 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008861 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008862/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8863 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008864 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008865 sp->prev = parsefile->strpush;
8866 parsefile->strpush = sp;
8867 } else
8868 sp = parsefile->strpush = &(parsefile->basestrpush);
8869 sp->prevstring = parsenextc;
8870 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008871#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008872 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008873 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008874 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008875 sp->string = s;
8876 }
Eric Andersen2870d962001-07-02 17:27:21 +00008877#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008878 parsenextc = s;
8879 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008880 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008881}
8882
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008883/*
8884 * To handle the "." command, a stack of input files is used. Pushfile
8885 * adds a new entry to the stack and popfile restores the previous level.
8886 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008887static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008888pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008889{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008890 struct parsefile *pf;
8891
8892 parsefile->nleft = parsenleft;
8893 parsefile->lleft = parselleft;
8894 parsefile->nextc = parsenextc;
8895 parsefile->linno = plinno;
8896 pf = ckmalloc(sizeof(*pf));
8897 pf->prev = parsefile;
8898 pf->fd = -1;
8899 pf->strpush = NULL;
8900 pf->basestrpush.prev = NULL;
8901 parsefile = pf;
8902}
8903
8904static void
8905popfile(void)
8906{
8907 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008908
Denis Vlasenkob012b102007-02-19 22:43:01 +00008909 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008910 if (pf->fd >= 0)
8911 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008912 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008913 while (pf->strpush)
8914 popstring();
8915 parsefile = pf->prev;
8916 free(pf);
8917 parsenleft = parsefile->nleft;
8918 parselleft = parsefile->lleft;
8919 parsenextc = parsefile->nextc;
8920 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008921 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008922}
8923
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008924/*
8925 * Return to top level.
8926 */
8927static void
8928popallfiles(void)
8929{
8930 while (parsefile != &basepf)
8931 popfile();
8932}
8933
8934/*
8935 * Close the file(s) that the shell is reading commands from. Called
8936 * after a fork is done.
8937 */
8938static void
8939closescript(void)
8940{
8941 popallfiles();
8942 if (parsefile->fd > 0) {
8943 close(parsefile->fd);
8944 parsefile->fd = 0;
8945 }
8946}
8947
8948/*
8949 * Like setinputfile, but takes an open file descriptor. Call this with
8950 * interrupts off.
8951 */
8952static void
8953setinputfd(int fd, int push)
8954{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008955 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008956 if (push) {
8957 pushfile();
8958 parsefile->buf = 0;
8959 }
8960 parsefile->fd = fd;
8961 if (parsefile->buf == NULL)
8962 parsefile->buf = ckmalloc(IBUFSIZ);
8963 parselleft = parsenleft = 0;
8964 plinno = 1;
8965}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008966
Eric Andersenc470f442003-07-28 09:56:35 +00008967/*
8968 * Set the input to take input from a file. If push is set, push the
8969 * old input onto the stack first.
8970 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008971static int
8972setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008973{
8974 int fd;
8975 int fd2;
8976
Denis Vlasenkob012b102007-02-19 22:43:01 +00008977 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008978 fd = open(fname, O_RDONLY);
8979 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008980 if (flags & INPUT_NOFILE_OK)
8981 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008982 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008983 }
Eric Andersenc470f442003-07-28 09:56:35 +00008984 if (fd < 10) {
8985 fd2 = copyfd(fd, 10);
8986 close(fd);
8987 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008988 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008989 fd = fd2;
8990 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008991 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008992 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008993 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008994 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00008995}
8996
Eric Andersencb57d552001-06-28 07:25:16 +00008997/*
8998 * Like setinputfile, but takes input from a string.
8999 */
Eric Andersenc470f442003-07-28 09:56:35 +00009000static void
9001setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009002{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009003 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009004 pushfile();
9005 parsenextc = string;
9006 parsenleft = strlen(string);
9007 parsefile->buf = NULL;
9008 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009009 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009010}
9011
9012
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009013/* ============ mail.c
9014 *
9015 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009016 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009017
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009018#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009019
Eric Andersencb57d552001-06-28 07:25:16 +00009020#define MAXMBOXES 10
9021
Eric Andersenc470f442003-07-28 09:56:35 +00009022/* times of mailboxes */
9023static time_t mailtime[MAXMBOXES];
9024/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009025static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009026
Eric Andersencb57d552001-06-28 07:25:16 +00009027/*
Eric Andersenc470f442003-07-28 09:56:35 +00009028 * Print appropriate message(s) if mail has arrived.
9029 * If mail_var_path_changed is set,
9030 * then the value of MAIL has mail_var_path_changed,
9031 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009032 */
Eric Andersenc470f442003-07-28 09:56:35 +00009033static void
9034chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009035{
Eric Andersencb57d552001-06-28 07:25:16 +00009036 const char *mpath;
9037 char *p;
9038 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009039 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009040 struct stackmark smark;
9041 struct stat statb;
9042
Eric Andersencb57d552001-06-28 07:25:16 +00009043 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009044 mpath = mpathset() ? mpathval() : mailval();
9045 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009046 p = padvance(&mpath, nullstr);
9047 if (p == NULL)
9048 break;
9049 if (*p == '\0')
9050 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009051 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009052#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009053 if (q[-1] != '/')
9054 abort();
9055#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009056 q[-1] = '\0'; /* delete trailing '/' */
9057 if (stat(p, &statb) < 0) {
9058 *mtp = 0;
9059 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009060 }
Eric Andersenc470f442003-07-28 09:56:35 +00009061 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9062 fprintf(
9063 stderr, snlfmt,
9064 pathopt ? pathopt : "you have mail"
9065 );
9066 }
9067 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009068 }
Eric Andersenc470f442003-07-28 09:56:35 +00009069 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009070 popstackmark(&smark);
9071}
Eric Andersencb57d552001-06-28 07:25:16 +00009072
Eric Andersenc470f442003-07-28 09:56:35 +00009073static void
9074changemail(const char *val)
9075{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009076 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009077}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009078
Denis Vlasenko131ae172007-02-18 13:00:19 +00009079#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009080
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009081
9082/* ============ ??? */
9083
Eric Andersencb57d552001-06-28 07:25:16 +00009084/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009085 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009086 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009087static void
9088setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009089{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009090 char **newparam;
9091 char **ap;
9092 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009093
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009094 for (nparam = 0; argv[nparam]; nparam++);
9095 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9096 while (*argv) {
9097 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009098 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009099 *ap = NULL;
9100 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009101 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009102 shellparam.nparam = nparam;
9103 shellparam.p = newparam;
9104#if ENABLE_ASH_GETOPTS
9105 shellparam.optind = 1;
9106 shellparam.optoff = -1;
9107#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009108}
9109
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009110/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009111 * Process shell options. The global variable argptr contains a pointer
9112 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009113 *
9114 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9115 * For a non-interactive shell, an error condition encountered
9116 * by a special built-in ... shall cause the shell to write a diagnostic message
9117 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009118 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009119 * ...
9120 * Utility syntax error (option or operand error) Shall exit
9121 * ...
9122 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9123 * we see that bash does not do that (set "finishes" with error code 1 instead,
9124 * and shell continues), and people rely on this behavior!
9125 * Testcase:
9126 * set -o barfoo 2>/dev/null
9127 * echo $?
9128 *
9129 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009130 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009131static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009132minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009133{
9134 int i;
9135
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009136 if (name) {
9137 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009138 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009139 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009140 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009141 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009142 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009143 ash_msg("illegal option -o %s", name);
9144 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009145 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009146 out1str("Current option settings\n");
9147 for (i = 0; i < NOPTS; i++)
9148 out1fmt("%-16s%s\n", optnames(i),
9149 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009150 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009151}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009152static void
9153setoption(int flag, int val)
9154{
9155 int i;
9156
9157 for (i = 0; i < NOPTS; i++) {
9158 if (optletters(i) == flag) {
9159 optlist[i] = val;
9160 return;
9161 }
9162 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009163 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009164 /* NOTREACHED */
9165}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009166static int
Eric Andersenc470f442003-07-28 09:56:35 +00009167options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009168{
9169 char *p;
9170 int val;
9171 int c;
9172
9173 if (cmdline)
9174 minusc = NULL;
9175 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009176 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009177 if (c != '-' && c != '+')
9178 break;
9179 argptr++;
9180 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009181 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009182 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009183 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009184 if (!cmdline) {
9185 /* "-" means turn off -x and -v */
9186 if (p[0] == '\0')
9187 xflag = vflag = 0;
9188 /* "--" means reset params */
9189 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009190 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009191 }
Eric Andersenc470f442003-07-28 09:56:35 +00009192 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009193 }
Eric Andersencb57d552001-06-28 07:25:16 +00009194 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009195 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009196 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009197 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009198 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009199 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009200 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009201 if (minus_o(*argptr, val)) {
9202 /* it already printed err message */
9203 return 1; /* error */
9204 }
Eric Andersencb57d552001-06-28 07:25:16 +00009205 if (*argptr)
9206 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009207 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9208 isloginsh = 1;
9209 /* bash does not accept +-login, we also won't */
9210 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009211 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009212 isloginsh = 1;
9213 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009214 } else {
9215 setoption(c, val);
9216 }
9217 }
9218 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009219 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009220}
9221
Eric Andersencb57d552001-06-28 07:25:16 +00009222/*
Eric Andersencb57d552001-06-28 07:25:16 +00009223 * The shift builtin command.
9224 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009225static int
Eric Andersenc470f442003-07-28 09:56:35 +00009226shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009227{
9228 int n;
9229 char **ap1, **ap2;
9230
9231 n = 1;
9232 if (argc > 1)
9233 n = number(argv[1]);
9234 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009235 ash_msg_and_raise_error("can't shift that many");
9236 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009237 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009238 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009239 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009240 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009241 }
9242 ap2 = shellparam.p;
9243 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009244#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009245 shellparam.optind = 1;
9246 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009247#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009248 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009249 return 0;
9250}
9251
Eric Andersencb57d552001-06-28 07:25:16 +00009252/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009253 * POSIX requires that 'set' (but not export or readonly) output the
9254 * variables in lexicographic order - by the locale's collating order (sigh).
9255 * Maybe we could keep them in an ordered balanced binary tree
9256 * instead of hashed lists.
9257 * For now just roll 'em through qsort for printing...
9258 */
9259static int
9260showvars(const char *sep_prefix, int on, int off)
9261{
9262 const char *sep;
9263 char **ep, **epend;
9264
9265 ep = listvars(on, off, &epend);
9266 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9267
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009268 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009269
9270 for (; ep < epend; ep++) {
9271 const char *p;
9272 const char *q;
9273
9274 p = strchrnul(*ep, '=');
9275 q = nullstr;
9276 if (*p)
9277 q = single_quote(++p);
9278 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9279 }
9280 return 0;
9281}
9282
9283/*
Eric Andersencb57d552001-06-28 07:25:16 +00009284 * The set command builtin.
9285 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009286static int
Eric Andersenc470f442003-07-28 09:56:35 +00009287setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009288{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009289 int retval;
9290
Eric Andersencb57d552001-06-28 07:25:16 +00009291 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009292 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009293 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009294 retval = 1;
9295 if (!options(0)) { /* if no parse error... */
9296 retval = 0;
9297 optschanged();
9298 if (*argptr != NULL) {
9299 setparam(argptr);
9300 }
Eric Andersencb57d552001-06-28 07:25:16 +00009301 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009302 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009303 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009304}
9305
Denis Vlasenko131ae172007-02-18 13:00:19 +00009306#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009307/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009308static void
9309change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009310{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009311 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009312 /* "get", generate */
9313 char buf[16];
9314
9315 rseed = rseed * 1103515245 + 12345;
9316 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9317 /* set without recursion */
9318 setvar(vrandom.text, buf, VNOFUNC);
9319 vrandom.flags &= ~VNOFUNC;
9320 } else {
9321 /* set/reset */
9322 rseed = strtoul(value, (char **)NULL, 10);
9323 }
Eric Andersenef02f822004-03-11 13:34:24 +00009324}
Eric Andersen16767e22004-03-16 05:14:10 +00009325#endif
9326
Denis Vlasenko131ae172007-02-18 13:00:19 +00009327#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009328static int
Eric Andersenc470f442003-07-28 09:56:35 +00009329getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009330{
9331 char *p, *q;
9332 char c = '?';
9333 int done = 0;
9334 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009335 char s[12];
9336 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009337
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009338 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009339 return 1;
9340 optnext = optfirst + *param_optind - 1;
9341
9342 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009343 p = NULL;
9344 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009345 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009346 if (p == NULL || *p == '\0') {
9347 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009348 p = *optnext;
9349 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009350 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009351 p = NULL;
9352 done = 1;
9353 goto out;
9354 }
9355 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009356 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009357 goto atend;
9358 }
9359
9360 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009361 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009362 if (*q == '\0') {
9363 if (optstr[0] == ':') {
9364 s[0] = c;
9365 s[1] = '\0';
9366 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009367 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009368 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009369 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009370 }
9371 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009372 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009373 }
9374 if (*++q == ':')
9375 q++;
9376 }
9377
9378 if (*++q == ':') {
9379 if (*p == '\0' && (p = *optnext) == NULL) {
9380 if (optstr[0] == ':') {
9381 s[0] = c;
9382 s[1] = '\0';
9383 err |= setvarsafe("OPTARG", s, 0);
9384 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009385 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009386 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009387 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009388 c = '?';
9389 }
Eric Andersenc470f442003-07-28 09:56:35 +00009390 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009391 }
9392
9393 if (p == *optnext)
9394 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009395 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009396 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009397 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009398 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009399 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009400 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009401 *param_optind = optnext - optfirst + 1;
9402 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009403 err |= setvarsafe("OPTIND", s, VNOFUNC);
9404 s[0] = c;
9405 s[1] = '\0';
9406 err |= setvarsafe(optvar, s, 0);
9407 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009408 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009409 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009410 flush_stdout_stderr();
9411 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009412 }
9413 return done;
9414}
Eric Andersenc470f442003-07-28 09:56:35 +00009415
9416/*
9417 * The getopts builtin. Shellparam.optnext points to the next argument
9418 * to be processed. Shellparam.optptr points to the next character to
9419 * be processed in the current argument. If shellparam.optnext is NULL,
9420 * then it's the first time getopts has been called.
9421 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009422static int
Eric Andersenc470f442003-07-28 09:56:35 +00009423getoptscmd(int argc, char **argv)
9424{
9425 char **optbase;
9426
9427 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009428 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009429 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009430 optbase = shellparam.p;
9431 if (shellparam.optind > shellparam.nparam + 1) {
9432 shellparam.optind = 1;
9433 shellparam.optoff = -1;
9434 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009435 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009436 optbase = &argv[3];
9437 if (shellparam.optind > argc - 2) {
9438 shellparam.optind = 1;
9439 shellparam.optoff = -1;
9440 }
9441 }
9442
9443 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009444 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009445}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009446#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009447
Eric Andersencb57d552001-06-28 07:25:16 +00009448
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009449/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009450
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009451/*
9452 * NEOF is returned by parsecmd when it encounters an end of file. It
9453 * must be distinct from NULL, so we use the address of a variable that
9454 * happens to be handy.
9455 */
9456static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009457#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009458static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009459static int lasttoken; /* last token read */
9460static char *wordtext; /* text of last word returned by readtoken */
9461static struct nodelist *backquotelist;
9462static union node *redirnode;
9463static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009464static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009465
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009466static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9467static void
9468raise_error_syntax(const char *msg)
9469{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009470 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009471 /* NOTREACHED */
9472}
9473
9474/*
9475 * Called when an unexpected token is read during the parse. The argument
9476 * is the token that is expected, or -1 if more than one type of token can
9477 * occur at this point.
9478 */
9479static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9480static void
9481raise_error_unexpected_syntax(int token)
9482{
9483 char msg[64];
9484 int l;
9485
9486 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9487 if (token >= 0)
9488 sprintf(msg + l, " (expecting %s)", tokname(token));
9489 raise_error_syntax(msg);
9490 /* NOTREACHED */
9491}
Eric Andersencb57d552001-06-28 07:25:16 +00009492
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009493#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009494
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009495struct heredoc {
9496 struct heredoc *next; /* next here document in list */
9497 union node *here; /* redirection node */
9498 char *eofmark; /* string indicating end of input */
9499 int striptabs; /* if set, strip leading tabs */
9500};
Eric Andersencb57d552001-06-28 07:25:16 +00009501
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009502static struct heredoc *heredoclist; /* list of here documents to read */
9503
9504/* parsing is heavily cross-recursive, need these forward decls */
9505static union node *andor(void);
9506static union node *pipeline(void);
9507static union node *parse_command(void);
9508static void parseheredoc(void);
9509static char peektoken(void);
9510static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009511
Eric Andersenc470f442003-07-28 09:56:35 +00009512static union node *
9513list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009514{
9515 union node *n1, *n2, *n3;
9516 int tok;
9517
Eric Andersenc470f442003-07-28 09:56:35 +00009518 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9519 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009520 return NULL;
9521 n1 = NULL;
9522 for (;;) {
9523 n2 = andor();
9524 tok = readtoken();
9525 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009526 if (n2->type == NPIPE) {
9527 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009528 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009529 if (n2->type != NREDIR) {
9530 n3 = stalloc(sizeof(struct nredir));
9531 n3->nredir.n = n2;
9532 n3->nredir.redirect = NULL;
9533 n2 = n3;
9534 }
9535 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009536 }
9537 }
9538 if (n1 == NULL) {
9539 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009540 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009541 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009542 n3->type = NSEMI;
9543 n3->nbinary.ch1 = n1;
9544 n3->nbinary.ch2 = n2;
9545 n1 = n3;
9546 }
9547 switch (tok) {
9548 case TBACKGND:
9549 case TSEMI:
9550 tok = readtoken();
9551 /* fall through */
9552 case TNL:
9553 if (tok == TNL) {
9554 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009555 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009556 return n1;
9557 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009558 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009559 }
Eric Andersenc470f442003-07-28 09:56:35 +00009560 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009561 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009562 return n1;
9563 break;
9564 case TEOF:
9565 if (heredoclist)
9566 parseheredoc();
9567 else
Eric Andersenc470f442003-07-28 09:56:35 +00009568 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009569 return n1;
9570 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009571 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009572 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009573 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009574 return n1;
9575 }
9576 }
9577}
9578
Eric Andersenc470f442003-07-28 09:56:35 +00009579static union node *
9580andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009581{
Eric Andersencb57d552001-06-28 07:25:16 +00009582 union node *n1, *n2, *n3;
9583 int t;
9584
Eric Andersencb57d552001-06-28 07:25:16 +00009585 n1 = pipeline();
9586 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009587 t = readtoken();
9588 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009589 t = NAND;
9590 } else if (t == TOR) {
9591 t = NOR;
9592 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009593 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009594 return n1;
9595 }
Eric Andersenc470f442003-07-28 09:56:35 +00009596 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009597 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009598 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009599 n3->type = t;
9600 n3->nbinary.ch1 = n1;
9601 n3->nbinary.ch2 = n2;
9602 n1 = n3;
9603 }
9604}
9605
Eric Andersenc470f442003-07-28 09:56:35 +00009606static union node *
9607pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009608{
Eric Andersencb57d552001-06-28 07:25:16 +00009609 union node *n1, *n2, *pipenode;
9610 struct nodelist *lp, *prev;
9611 int negate;
9612
9613 negate = 0;
9614 TRACE(("pipeline: entered\n"));
9615 if (readtoken() == TNOT) {
9616 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009617 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009618 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009619 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009620 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009621 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009622 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009623 pipenode->type = NPIPE;
9624 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009625 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009626 pipenode->npipe.cmdlist = lp;
9627 lp->n = n1;
9628 do {
9629 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009630 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009631 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009632 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009633 prev->next = lp;
9634 } while (readtoken() == TPIPE);
9635 lp->next = NULL;
9636 n1 = pipenode;
9637 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009638 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009639 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009640 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009641 n2->type = NNOT;
9642 n2->nnot.com = n1;
9643 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009644 }
9645 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009646}
9647
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009648static union node *
9649makename(void)
9650{
9651 union node *n;
9652
9653 n = stalloc(sizeof(struct narg));
9654 n->type = NARG;
9655 n->narg.next = NULL;
9656 n->narg.text = wordtext;
9657 n->narg.backquote = backquotelist;
9658 return n;
9659}
9660
9661static void
9662fixredir(union node *n, const char *text, int err)
9663{
9664 TRACE(("Fix redir %s %d\n", text, err));
9665 if (!err)
9666 n->ndup.vname = NULL;
9667
9668 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009669 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009670 else if (LONE_DASH(text))
9671 n->ndup.dupfd = -1;
9672 else {
9673 if (err)
9674 raise_error_syntax("Bad fd number");
9675 n->ndup.vname = makename();
9676 }
9677}
9678
9679/*
9680 * Returns true if the text contains nothing to expand (no dollar signs
9681 * or backquotes).
9682 */
9683static int
9684noexpand(char *text)
9685{
9686 char *p;
9687 char c;
9688
9689 p = text;
9690 while ((c = *p++) != '\0') {
9691 if (c == CTLQUOTEMARK)
9692 continue;
9693 if (c == CTLESC)
9694 p++;
9695 else if (SIT(c, BASESYNTAX) == CCTL)
9696 return 0;
9697 }
9698 return 1;
9699}
9700
9701static void
9702parsefname(void)
9703{
9704 union node *n = redirnode;
9705
9706 if (readtoken() != TWORD)
9707 raise_error_unexpected_syntax(-1);
9708 if (n->type == NHERE) {
9709 struct heredoc *here = heredoc;
9710 struct heredoc *p;
9711 int i;
9712
9713 if (quoteflag == 0)
9714 n->type = NXHERE;
9715 TRACE(("Here document %d\n", n->type));
9716 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9717 raise_error_syntax("Illegal eof marker for << redirection");
9718 rmescapes(wordtext);
9719 here->eofmark = wordtext;
9720 here->next = NULL;
9721 if (heredoclist == NULL)
9722 heredoclist = here;
9723 else {
9724 for (p = heredoclist; p->next; p = p->next);
9725 p->next = here;
9726 }
9727 } else if (n->type == NTOFD || n->type == NFROMFD) {
9728 fixredir(n, wordtext, 0);
9729 } else {
9730 n->nfile.fname = makename();
9731 }
9732}
Eric Andersencb57d552001-06-28 07:25:16 +00009733
Eric Andersenc470f442003-07-28 09:56:35 +00009734static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009735simplecmd(void)
9736{
9737 union node *args, **app;
9738 union node *n = NULL;
9739 union node *vars, **vpp;
9740 union node **rpp, *redir;
9741 int savecheckkwd;
9742
9743 args = NULL;
9744 app = &args;
9745 vars = NULL;
9746 vpp = &vars;
9747 redir = NULL;
9748 rpp = &redir;
9749
9750 savecheckkwd = CHKALIAS;
9751 for (;;) {
9752 checkkwd = savecheckkwd;
9753 switch (readtoken()) {
9754 case TWORD:
9755 n = stalloc(sizeof(struct narg));
9756 n->type = NARG;
9757 n->narg.text = wordtext;
9758 n->narg.backquote = backquotelist;
9759 if (savecheckkwd && isassignment(wordtext)) {
9760 *vpp = n;
9761 vpp = &n->narg.next;
9762 } else {
9763 *app = n;
9764 app = &n->narg.next;
9765 savecheckkwd = 0;
9766 }
9767 break;
9768 case TREDIR:
9769 *rpp = n = redirnode;
9770 rpp = &n->nfile.next;
9771 parsefname(); /* read name of redirection file */
9772 break;
9773 case TLP:
9774 if (args && app == &args->narg.next
9775 && !vars && !redir
9776 ) {
9777 struct builtincmd *bcmd;
9778 const char *name;
9779
9780 /* We have a function */
9781 if (readtoken() != TRP)
9782 raise_error_unexpected_syntax(TRP);
9783 name = n->narg.text;
9784 if (!goodname(name)
9785 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9786 ) {
9787 raise_error_syntax("Bad function name");
9788 }
9789 n->type = NDEFUN;
9790 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9791 n->narg.next = parse_command();
9792 return n;
9793 }
9794 /* fall through */
9795 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009796 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009797 goto out;
9798 }
9799 }
9800 out:
9801 *app = NULL;
9802 *vpp = NULL;
9803 *rpp = NULL;
9804 n = stalloc(sizeof(struct ncmd));
9805 n->type = NCMD;
9806 n->ncmd.args = args;
9807 n->ncmd.assign = vars;
9808 n->ncmd.redirect = redir;
9809 return n;
9810}
9811
9812static union node *
9813parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009814{
Eric Andersencb57d552001-06-28 07:25:16 +00009815 union node *n1, *n2;
9816 union node *ap, **app;
9817 union node *cp, **cpp;
9818 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009819 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009820 int t;
9821
9822 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009823 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009824
Eric Andersencb57d552001-06-28 07:25:16 +00009825 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009826 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009827 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009828 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009829 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009830 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009831 n1->type = NIF;
9832 n1->nif.test = list(0);
9833 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009834 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009835 n1->nif.ifpart = list(0);
9836 n2 = n1;
9837 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009838 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009839 n2 = n2->nif.elsepart;
9840 n2->type = NIF;
9841 n2->nif.test = list(0);
9842 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009843 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009844 n2->nif.ifpart = list(0);
9845 }
9846 if (lasttoken == TELSE)
9847 n2->nif.elsepart = list(0);
9848 else {
9849 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009850 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009851 }
Eric Andersenc470f442003-07-28 09:56:35 +00009852 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009853 break;
9854 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009855 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009856 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009857 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009858 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009859 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009860 got = readtoken();
9861 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009862 TRACE(("expecting DO got %s %s\n", tokname(got),
9863 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009864 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009865 }
9866 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009867 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009868 break;
9869 }
9870 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009871 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009872 raise_error_syntax("Bad for loop variable");
9873 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009874 n1->type = NFOR;
9875 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009876 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009877 if (readtoken() == TIN) {
9878 app = &ap;
9879 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009880 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009881 n2->type = NARG;
9882 n2->narg.text = wordtext;
9883 n2->narg.backquote = backquotelist;
9884 *app = n2;
9885 app = &n2->narg.next;
9886 }
9887 *app = NULL;
9888 n1->nfor.args = ap;
9889 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009890 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009891 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009892 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009893 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009894 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009895 n2->narg.backquote = NULL;
9896 n2->narg.next = NULL;
9897 n1->nfor.args = n2;
9898 /*
9899 * Newline or semicolon here is optional (but note
9900 * that the original Bourne shell only allowed NL).
9901 */
9902 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009903 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009904 }
Eric Andersenc470f442003-07-28 09:56:35 +00009905 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009906 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009907 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009908 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009909 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009910 break;
9911 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009912 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009913 n1->type = NCASE;
9914 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009915 raise_error_unexpected_syntax(TWORD);
9916 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009917 n2->type = NARG;
9918 n2->narg.text = wordtext;
9919 n2->narg.backquote = backquotelist;
9920 n2->narg.next = NULL;
9921 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009922 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009923 } while (readtoken() == TNL);
9924 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009925 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009926 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009927 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009928 checkkwd = CHKNL | CHKKWD;
9929 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009930 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009931 if (lasttoken == TLP)
9932 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009933 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009934 cp->type = NCLIST;
9935 app = &cp->nclist.pattern;
9936 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009937 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009938 ap->type = NARG;
9939 ap->narg.text = wordtext;
9940 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009941 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009942 break;
9943 app = &ap->narg.next;
9944 readtoken();
9945 }
9946 ap->narg.next = NULL;
9947 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009948 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009949 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009950
Eric Andersenc470f442003-07-28 09:56:35 +00009951 cpp = &cp->nclist.next;
9952
9953 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009954 t = readtoken();
9955 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009956 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009957 raise_error_unexpected_syntax(TENDCASE);
9958 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009959 }
Eric Andersenc470f442003-07-28 09:56:35 +00009960 }
Eric Andersencb57d552001-06-28 07:25:16 +00009961 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009962 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009963 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009964 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009965 n1->type = NSUBSHELL;
9966 n1->nredir.n = list(0);
9967 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009968 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009969 break;
9970 case TBEGIN:
9971 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009972 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009974 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009975 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009976 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009977 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009978 }
9979
Eric Andersenc470f442003-07-28 09:56:35 +00009980 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009981 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009982
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009983 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009984 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009985 checkkwd = CHKKWD | CHKALIAS;
9986 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009987 while (readtoken() == TREDIR) {
9988 *rpp = n2 = redirnode;
9989 rpp = &n2->nfile.next;
9990 parsefname();
9991 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009992 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009993 *rpp = NULL;
9994 if (redir) {
9995 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009996 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009997 n2->type = NREDIR;
9998 n2->nredir.n = n1;
9999 n1 = n2;
10000 }
10001 n1->nredir.redirect = redir;
10002 }
Eric Andersencb57d552001-06-28 07:25:16 +000010003 return n1;
10004}
10005
Eric Andersencb57d552001-06-28 07:25:16 +000010006/*
10007 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10008 * is not NULL, read a here document. In the latter case, eofmark is the
10009 * word which marks the end of the document and striptabs is true if
10010 * leading tabs should be stripped from the document. The argument firstc
10011 * is the first character of the input token or document.
10012 *
10013 * Because C does not have internal subroutines, I have simulated them
10014 * using goto's to implement the subroutine linkage. The following macros
10015 * will run code that appears at the end of readtoken1.
10016 */
10017
Eric Andersen2870d962001-07-02 17:27:21 +000010018#define CHECKEND() {goto checkend; checkend_return:;}
10019#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10020#define PARSESUB() {goto parsesub; parsesub_return:;}
10021#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10022#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10023#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010024
10025static int
Eric Andersenc470f442003-07-28 09:56:35 +000010026readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010027{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010028 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010029 int c = firstc;
10030 char *out;
10031 int len;
10032 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010033 struct nodelist *bqlist;
10034 smallint quotef;
10035 smallint dblquote;
10036 smallint oldstyle;
10037 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010038#if ENABLE_ASH_EXPAND_PRMT
10039 smallint pssyntax; /* we are expanding a prompt string */
10040#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010041 int varnest; /* levels of variables expansion */
10042 int arinest; /* levels of arithmetic expansion */
10043 int parenlevel; /* levels of parens in arithmetic */
10044 int dqvarnest; /* levels of variables expansion within double quotes */
10045
Eric Andersencb57d552001-06-28 07:25:16 +000010046#if __GNUC__
10047 /* Avoid longjmp clobbering */
10048 (void) &out;
10049 (void) &quotef;
10050 (void) &dblquote;
10051 (void) &varnest;
10052 (void) &arinest;
10053 (void) &parenlevel;
10054 (void) &dqvarnest;
10055 (void) &oldstyle;
10056 (void) &prevsyntax;
10057 (void) &syntax;
10058#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010059 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010060 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010061 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010062 oldstyle = 0;
10063 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010064#if ENABLE_ASH_EXPAND_PRMT
10065 pssyntax = (syntax == PSSYNTAX);
10066 if (pssyntax)
10067 syntax = DQSYNTAX;
10068#endif
10069 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010070 varnest = 0;
10071 arinest = 0;
10072 parenlevel = 0;
10073 dqvarnest = 0;
10074
10075 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010076 loop: { /* for each line, until end of word */
10077 CHECKEND(); /* set c to PEOF if at end of here document */
10078 for (;;) { /* until end of line or end of word */
10079 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010080 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010081 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010082 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010083 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010084 USTPUTC(c, out);
10085 plinno++;
10086 if (doprompt)
10087 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010088 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010089 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010090 case CWORD:
10091 USTPUTC(c, out);
10092 break;
10093 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010094 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010095 USTPUTC(CTLESC, out);
10096 USTPUTC(c, out);
10097 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010098 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010099 c = pgetc2();
10100 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010101 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010102 USTPUTC('\\', out);
10103 pungetc();
10104 } else if (c == '\n') {
10105 if (doprompt)
10106 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010107 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010108#if ENABLE_ASH_EXPAND_PRMT
10109 if (c == '$' && pssyntax) {
10110 USTPUTC(CTLESC, out);
10111 USTPUTC('\\', out);
10112 }
10113#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010114 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010115 c != '\\' && c != '`' &&
10116 c != '$' && (
10117 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010118 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010119 ) {
10120 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010121 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010122 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010123 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010124 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010125 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010126 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010127 }
10128 break;
10129 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010130 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010131 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010132 if (eofmark == NULL) {
10133 USTPUTC(CTLQUOTEMARK, out);
10134 }
Eric Andersencb57d552001-06-28 07:25:16 +000010135 break;
10136 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010137 syntax = DQSYNTAX;
10138 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010139 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010140 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010141 if (eofmark != NULL && arinest == 0
10142 && varnest == 0
10143 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010144 USTPUTC(c, out);
10145 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010146 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010147 syntax = BASESYNTAX;
10148 dblquote = 0;
10149 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010150 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010151 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010152 }
10153 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010154 case CVAR: /* '$' */
10155 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010156 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010157 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010158 if (varnest > 0) {
10159 varnest--;
10160 if (dqvarnest > 0) {
10161 dqvarnest--;
10162 }
10163 USTPUTC(CTLENDVAR, out);
10164 } else {
10165 USTPUTC(c, out);
10166 }
10167 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010168#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010169 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010170 parenlevel++;
10171 USTPUTC(c, out);
10172 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010173 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010174 if (parenlevel > 0) {
10175 USTPUTC(c, out);
10176 --parenlevel;
10177 } else {
10178 if (pgetc() == ')') {
10179 if (--arinest == 0) {
10180 USTPUTC(CTLENDARI, out);
10181 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010182 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010183 } else
10184 USTPUTC(')', out);
10185 } else {
10186 /*
10187 * unbalanced parens
10188 * (don't 2nd guess - no error)
10189 */
10190 pungetc();
10191 USTPUTC(')', out);
10192 }
10193 }
10194 break;
10195#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010196 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010197 PARSEBACKQOLD();
10198 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010199 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010200 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010201 case CIGN:
10202 break;
10203 default:
10204 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010205 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010206#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010207 if (c != PEOA)
10208#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010209 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010210
Eric Andersencb57d552001-06-28 07:25:16 +000010211 }
10212 c = pgetc_macro();
10213 }
10214 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010215 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010216#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010217 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010218 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010219#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010220 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010221 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010222 if (varnest != 0) {
10223 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010224 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010225 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010226 }
10227 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010228 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010229 out = stackblock();
10230 if (eofmark == NULL) {
10231 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010232 && quotef == 0
10233 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010234 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010235 PARSEREDIR();
10236 return lasttoken = TREDIR;
10237 } else {
10238 pungetc();
10239 }
10240 }
10241 quoteflag = quotef;
10242 backquotelist = bqlist;
10243 grabstackblock(len);
10244 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010245 lasttoken = TWORD;
10246 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010247/* end of readtoken routine */
10248
Eric Andersencb57d552001-06-28 07:25:16 +000010249/*
10250 * Check to see whether we are at the end of the here document. When this
10251 * is called, c is set to the first character of the next input line. If
10252 * we are at the end of the here document, this routine sets the c to PEOF.
10253 */
Eric Andersenc470f442003-07-28 09:56:35 +000010254checkend: {
10255 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010256#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010257 if (c == PEOA) {
10258 c = pgetc2();
10259 }
10260#endif
10261 if (striptabs) {
10262 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010263 c = pgetc2();
10264 }
Eric Andersenc470f442003-07-28 09:56:35 +000010265 }
10266 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010267 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010268 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010269
Eric Andersenc470f442003-07-28 09:56:35 +000010270 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010271 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010272 if (*p == '\n' && *q == '\0') {
10273 c = PEOF;
10274 plinno++;
10275 needprompt = doprompt;
10276 } else {
10277 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010278 }
10279 }
10280 }
10281 }
Eric Andersenc470f442003-07-28 09:56:35 +000010282 goto checkend_return;
10283}
Eric Andersencb57d552001-06-28 07:25:16 +000010284
Eric Andersencb57d552001-06-28 07:25:16 +000010285/*
10286 * Parse a redirection operator. The variable "out" points to a string
10287 * specifying the fd to be redirected. The variable "c" contains the
10288 * first character of the redirection operator.
10289 */
Eric Andersenc470f442003-07-28 09:56:35 +000010290parseredir: {
10291 char fd = *out;
10292 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010293
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010294 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010295 if (c == '>') {
10296 np->nfile.fd = 1;
10297 c = pgetc();
10298 if (c == '>')
10299 np->type = NAPPEND;
10300 else if (c == '|')
10301 np->type = NCLOBBER;
10302 else if (c == '&')
10303 np->type = NTOFD;
10304 else {
10305 np->type = NTO;
10306 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010307 }
Eric Andersenc470f442003-07-28 09:56:35 +000010308 } else { /* c == '<' */
10309 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010310 c = pgetc();
10311 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010312 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010313 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010314 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010315 np->nfile.fd = 0;
10316 }
10317 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010318 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010319 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010320 c = pgetc();
10321 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010322 heredoc->striptabs = 1;
10323 } else {
10324 heredoc->striptabs = 0;
10325 pungetc();
10326 }
10327 break;
10328
10329 case '&':
10330 np->type = NFROMFD;
10331 break;
10332
10333 case '>':
10334 np->type = NFROMTO;
10335 break;
10336
10337 default:
10338 np->type = NFROM;
10339 pungetc();
10340 break;
10341 }
Eric Andersencb57d552001-06-28 07:25:16 +000010342 }
Eric Andersenc470f442003-07-28 09:56:35 +000010343 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010344 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010345 redirnode = np;
10346 goto parseredir_return;
10347}
Eric Andersencb57d552001-06-28 07:25:16 +000010348
Eric Andersencb57d552001-06-28 07:25:16 +000010349/*
10350 * Parse a substitution. At this point, we have read the dollar sign
10351 * and nothing else.
10352 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010353
10354/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10355 * (assuming ascii char codes, as the original implementation did) */
10356#define is_special(c) \
10357 ((((unsigned int)c) - 33 < 32) \
10358 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010359parsesub: {
10360 int subtype;
10361 int typeloc;
10362 int flags;
10363 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010364 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010365
Eric Andersenc470f442003-07-28 09:56:35 +000010366 c = pgetc();
10367 if (
10368 c <= PEOA_OR_PEOF ||
10369 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10370 ) {
10371 USTPUTC('$', out);
10372 pungetc();
10373 } else if (c == '(') { /* $(command) or $((arith)) */
10374 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010375#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010376 PARSEARITH();
10377#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010378 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010379#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010380 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010381 pungetc();
10382 PARSEBACKQNEW();
10383 }
10384 } else {
10385 USTPUTC(CTLVAR, out);
10386 typeloc = out - (char *)stackblock();
10387 USTPUTC(VSNORMAL, out);
10388 subtype = VSNORMAL;
10389 if (c == '{') {
10390 c = pgetc();
10391 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010392 c = pgetc();
10393 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010394 c = '#';
10395 else
10396 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010397 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010398 subtype = 0;
10399 }
10400 if (c > PEOA_OR_PEOF && is_name(c)) {
10401 do {
10402 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010403 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010404 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010405 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010406 do {
10407 STPUTC(c, out);
10408 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010409 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010410 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010411 USTPUTC(c, out);
10412 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010413 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010414 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010415
Eric Andersenc470f442003-07-28 09:56:35 +000010416 STPUTC('=', out);
10417 flags = 0;
10418 if (subtype == 0) {
10419 switch (c) {
10420 case ':':
10421 flags = VSNUL;
10422 c = pgetc();
10423 /*FALLTHROUGH*/
10424 default:
10425 p = strchr(types, c);
10426 if (p == NULL)
10427 goto badsub;
10428 subtype = p - types + VSNORMAL;
10429 break;
10430 case '%':
10431 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010432 {
10433 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010434 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010435 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010436 c = pgetc();
10437 if (c == cc)
10438 subtype++;
10439 else
10440 pungetc();
10441 break;
10442 }
10443 }
Eric Andersenc470f442003-07-28 09:56:35 +000010444 } else {
10445 pungetc();
10446 }
10447 if (dblquote || arinest)
10448 flags |= VSQUOTE;
10449 *((char *)stackblock() + typeloc) = subtype | flags;
10450 if (subtype != VSNORMAL) {
10451 varnest++;
10452 if (dblquote || arinest) {
10453 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010454 }
10455 }
10456 }
Eric Andersenc470f442003-07-28 09:56:35 +000010457 goto parsesub_return;
10458}
Eric Andersencb57d552001-06-28 07:25:16 +000010459
Eric Andersencb57d552001-06-28 07:25:16 +000010460/*
10461 * Called to parse command substitutions. Newstyle is set if the command
10462 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10463 * list of commands (passed by reference), and savelen is the number of
10464 * characters on the top of the stack which must be preserved.
10465 */
Eric Andersenc470f442003-07-28 09:56:35 +000010466parsebackq: {
10467 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010468 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010469 union node *n;
10470 char *volatile str;
10471 struct jmploc jmploc;
10472 struct jmploc *volatile savehandler;
10473 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010474 smallint saveprompt = 0;
10475
Eric Andersencb57d552001-06-28 07:25:16 +000010476#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010477 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010478#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010479 savepbq = parsebackquote;
10480 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010481 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010482 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010483 exception_handler = savehandler;
10484 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010485 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010486 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010487 str = NULL;
10488 savelen = out - (char *)stackblock();
10489 if (savelen > 0) {
10490 str = ckmalloc(savelen);
10491 memcpy(str, stackblock(), savelen);
10492 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010493 savehandler = exception_handler;
10494 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010495 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010496 if (oldstyle) {
10497 /* We must read until the closing backquote, giving special
10498 treatment to some slashes, and then push the string and
10499 reread it as input, interpreting it normally. */
10500 char *pout;
10501 int pc;
10502 size_t psavelen;
10503 char *pstr;
10504
10505
10506 STARTSTACKSTR(pout);
10507 for (;;) {
10508 if (needprompt) {
10509 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010510 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010511 pc = pgetc();
10512 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010513 case '`':
10514 goto done;
10515
10516 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010517 pc = pgetc();
10518 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010519 plinno++;
10520 if (doprompt)
10521 setprompt(2);
10522 /*
10523 * If eating a newline, avoid putting
10524 * the newline into the new character
10525 * stream (via the STPUTC after the
10526 * switch).
10527 */
10528 continue;
10529 }
10530 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010531 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010532 STPUTC('\\', pout);
10533 if (pc > PEOA_OR_PEOF) {
10534 break;
10535 }
10536 /* fall through */
10537
10538 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010539#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010540 case PEOA:
10541#endif
10542 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010543 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010544
10545 case '\n':
10546 plinno++;
10547 needprompt = doprompt;
10548 break;
10549
10550 default:
10551 break;
10552 }
10553 STPUTC(pc, pout);
10554 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010555 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010556 STPUTC('\0', pout);
10557 psavelen = pout - (char *)stackblock();
10558 if (psavelen > 0) {
10559 pstr = grabstackstr(pout);
10560 setinputstring(pstr);
10561 }
10562 }
10563 nlpp = &bqlist;
10564 while (*nlpp)
10565 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010566 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010567 (*nlpp)->next = NULL;
10568 parsebackquote = oldstyle;
10569
10570 if (oldstyle) {
10571 saveprompt = doprompt;
10572 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010573 }
10574
Eric Andersenc470f442003-07-28 09:56:35 +000010575 n = list(2);
10576
10577 if (oldstyle)
10578 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010579 else if (readtoken() != TRP)
10580 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010581
10582 (*nlpp)->n = n;
10583 if (oldstyle) {
10584 /*
10585 * Start reading from old file again, ignoring any pushed back
10586 * tokens left from the backquote parsing
10587 */
10588 popfile();
10589 tokpushback = 0;
10590 }
10591 while (stackblocksize() <= savelen)
10592 growstackblock();
10593 STARTSTACKSTR(out);
10594 if (str) {
10595 memcpy(out, str, savelen);
10596 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010597 INT_OFF;
10598 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010599 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010600 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010601 }
10602 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010603 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010604 if (arinest || dblquote)
10605 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10606 else
10607 USTPUTC(CTLBACKQ, out);
10608 if (oldstyle)
10609 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010610 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010611}
10612
Denis Vlasenko131ae172007-02-18 13:00:19 +000010613#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010614/*
10615 * Parse an arithmetic expansion (indicate start of one and set state)
10616 */
Eric Andersenc470f442003-07-28 09:56:35 +000010617parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010618 if (++arinest == 1) {
10619 prevsyntax = syntax;
10620 syntax = ARISYNTAX;
10621 USTPUTC(CTLARI, out);
10622 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010623 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010624 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010625 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010626 } else {
10627 /*
10628 * we collapse embedded arithmetic expansion to
10629 * parenthesis, which should be equivalent
10630 */
10631 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010632 }
Eric Andersenc470f442003-07-28 09:56:35 +000010633 goto parsearith_return;
10634}
10635#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010636
Eric Andersenc470f442003-07-28 09:56:35 +000010637} /* end of readtoken */
10638
Eric Andersencb57d552001-06-28 07:25:16 +000010639/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 * Read the next input token.
10641 * If the token is a word, we set backquotelist to the list of cmds in
10642 * backquotes. We set quoteflag to true if any part of the word was
10643 * quoted.
10644 * If the token is TREDIR, then we set redirnode to a structure containing
10645 * the redirection.
10646 * In all cases, the variable startlinno is set to the number of the line
10647 * on which the token starts.
10648 *
10649 * [Change comment: here documents and internal procedures]
10650 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10651 * word parsing code into a separate routine. In this case, readtoken
10652 * doesn't need to have any internal procedures, but parseword does.
10653 * We could also make parseoperator in essence the main routine, and
10654 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010655 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010656#define NEW_xxreadtoken
10657#ifdef NEW_xxreadtoken
10658/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010659static const char xxreadtoken_chars[7] ALIGN1 = {
10660 '\n', '(', ')', '&', '|', ';', 0
10661};
Eric Andersencb57d552001-06-28 07:25:16 +000010662
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010663static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010664 TNL, TLP, TRP, /* only single occurrence allowed */
10665 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10666 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010667 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010668};
10669
10670#define xxreadtoken_doubles \
10671 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10672#define xxreadtoken_singles \
10673 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10674
10675static int
10676xxreadtoken(void)
10677{
10678 int c;
10679
10680 if (tokpushback) {
10681 tokpushback = 0;
10682 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010683 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010684 if (needprompt) {
10685 setprompt(2);
10686 }
10687 startlinno = plinno;
10688 for (;;) { /* until token or start of word found */
10689 c = pgetc_macro();
10690
10691 if ((c != ' ') && (c != '\t')
10692#if ENABLE_ASH_ALIAS
10693 && (c != PEOA)
10694#endif
10695 ) {
10696 if (c == '#') {
10697 while ((c = pgetc()) != '\n' && c != PEOF);
10698 pungetc();
10699 } else if (c == '\\') {
10700 if (pgetc() != '\n') {
10701 pungetc();
10702 goto READTOKEN1;
10703 }
10704 startlinno = ++plinno;
10705 if (doprompt)
10706 setprompt(2);
10707 } else {
10708 const char *p
10709 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10710
10711 if (c != PEOF) {
10712 if (c == '\n') {
10713 plinno++;
10714 needprompt = doprompt;
10715 }
10716
10717 p = strchr(xxreadtoken_chars, c);
10718 if (p == NULL) {
10719 READTOKEN1:
10720 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10721 }
10722
10723 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10724 if (pgetc() == *p) { /* double occurrence? */
10725 p += xxreadtoken_doubles + 1;
10726 } else {
10727 pungetc();
10728 }
10729 }
10730 }
10731 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10732 }
10733 }
10734 } /* for */
10735}
10736#else
10737#define RETURN(token) return lasttoken = token
10738static int
10739xxreadtoken(void)
10740{
10741 int c;
10742
10743 if (tokpushback) {
10744 tokpushback = 0;
10745 return lasttoken;
10746 }
10747 if (needprompt) {
10748 setprompt(2);
10749 }
10750 startlinno = plinno;
10751 for (;;) { /* until token or start of word found */
10752 c = pgetc_macro();
10753 switch (c) {
10754 case ' ': case '\t':
10755#if ENABLE_ASH_ALIAS
10756 case PEOA:
10757#endif
10758 continue;
10759 case '#':
10760 while ((c = pgetc()) != '\n' && c != PEOF);
10761 pungetc();
10762 continue;
10763 case '\\':
10764 if (pgetc() == '\n') {
10765 startlinno = ++plinno;
10766 if (doprompt)
10767 setprompt(2);
10768 continue;
10769 }
10770 pungetc();
10771 goto breakloop;
10772 case '\n':
10773 plinno++;
10774 needprompt = doprompt;
10775 RETURN(TNL);
10776 case PEOF:
10777 RETURN(TEOF);
10778 case '&':
10779 if (pgetc() == '&')
10780 RETURN(TAND);
10781 pungetc();
10782 RETURN(TBACKGND);
10783 case '|':
10784 if (pgetc() == '|')
10785 RETURN(TOR);
10786 pungetc();
10787 RETURN(TPIPE);
10788 case ';':
10789 if (pgetc() == ';')
10790 RETURN(TENDCASE);
10791 pungetc();
10792 RETURN(TSEMI);
10793 case '(':
10794 RETURN(TLP);
10795 case ')':
10796 RETURN(TRP);
10797 default:
10798 goto breakloop;
10799 }
10800 }
10801 breakloop:
10802 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10803#undef RETURN
10804}
10805#endif /* NEW_xxreadtoken */
10806
10807static int
10808readtoken(void)
10809{
10810 int t;
10811#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010812 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010813#endif
10814
10815#if ENABLE_ASH_ALIAS
10816 top:
10817#endif
10818
10819 t = xxreadtoken();
10820
10821 /*
10822 * eat newlines
10823 */
10824 if (checkkwd & CHKNL) {
10825 while (t == TNL) {
10826 parseheredoc();
10827 t = xxreadtoken();
10828 }
10829 }
10830
10831 if (t != TWORD || quoteflag) {
10832 goto out;
10833 }
10834
10835 /*
10836 * check for keywords
10837 */
10838 if (checkkwd & CHKKWD) {
10839 const char *const *pp;
10840
10841 pp = findkwd(wordtext);
10842 if (pp) {
10843 lasttoken = t = pp - tokname_array;
10844 TRACE(("keyword %s recognized\n", tokname(t)));
10845 goto out;
10846 }
10847 }
10848
10849 if (checkkwd & CHKALIAS) {
10850#if ENABLE_ASH_ALIAS
10851 struct alias *ap;
10852 ap = lookupalias(wordtext, 1);
10853 if (ap != NULL) {
10854 if (*ap->val) {
10855 pushstring(ap->val, ap);
10856 }
10857 goto top;
10858 }
10859#endif
10860 }
10861 out:
10862 checkkwd = 0;
10863#if DEBUG
10864 if (!alreadyseen)
10865 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10866 else
10867 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10868#endif
10869 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010870}
10871
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010872static char
10873peektoken(void)
10874{
10875 int t;
10876
10877 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010878 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010879 return tokname_array[t][0];
10880}
Eric Andersencb57d552001-06-28 07:25:16 +000010881
10882/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010883 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10884 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010885 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010886static union node *
10887parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010888{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010889 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010890
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010891 tokpushback = 0;
10892 doprompt = interact;
10893 if (doprompt)
10894 setprompt(doprompt);
10895 needprompt = 0;
10896 t = readtoken();
10897 if (t == TEOF)
10898 return NEOF;
10899 if (t == TNL)
10900 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010901 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010902 return list(1);
10903}
10904
10905/*
10906 * Input any here documents.
10907 */
10908static void
10909parseheredoc(void)
10910{
10911 struct heredoc *here;
10912 union node *n;
10913
10914 here = heredoclist;
10915 heredoclist = 0;
10916
10917 while (here) {
10918 if (needprompt) {
10919 setprompt(2);
10920 }
10921 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10922 here->eofmark, here->striptabs);
10923 n = stalloc(sizeof(struct narg));
10924 n->narg.type = NARG;
10925 n->narg.next = NULL;
10926 n->narg.text = wordtext;
10927 n->narg.backquote = backquotelist;
10928 here->here->nhere.doc = n;
10929 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010930 }
Eric Andersencb57d552001-06-28 07:25:16 +000010931}
10932
10933
10934/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010935 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010936 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010937#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010938static const char *
10939expandstr(const char *ps)
10940{
10941 union node n;
10942
10943 /* XXX Fix (char *) cast. */
10944 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010945 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010946 popfile();
10947
10948 n.narg.type = NARG;
10949 n.narg.next = NULL;
10950 n.narg.text = wordtext;
10951 n.narg.backquote = backquotelist;
10952
10953 expandarg(&n, NULL, 0);
10954 return stackblock();
10955}
10956#endif
10957
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010958/*
10959 * Execute a command or commands contained in a string.
10960 */
10961static int
10962evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010963{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010964 union node *n;
10965 struct stackmark smark;
10966 int skip;
10967
10968 setinputstring(s);
10969 setstackmark(&smark);
10970
10971 skip = 0;
10972 while ((n = parsecmd(0)) != NEOF) {
10973 evaltree(n, 0);
10974 popstackmark(&smark);
10975 skip = evalskip;
10976 if (skip)
10977 break;
10978 }
10979 popfile();
10980
10981 skip &= mask;
10982 evalskip = skip;
10983 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010984}
10985
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010986/*
10987 * The eval command.
10988 */
10989static int
10990evalcmd(int argc, char **argv)
10991{
10992 char *p;
10993 char *concat;
10994 char **ap;
10995
10996 if (argc > 1) {
10997 p = argv[1];
10998 if (argc > 2) {
10999 STARTSTACKSTR(concat);
11000 ap = argv + 2;
11001 for (;;) {
11002 concat = stack_putstr(p, concat);
11003 p = *ap++;
11004 if (p == NULL)
11005 break;
11006 STPUTC(' ', concat);
11007 }
11008 STPUTC('\0', concat);
11009 p = grabstackstr(concat);
11010 }
11011 evalstring(p, ~SKIPEVAL);
11012
11013 }
11014 return exitstatus;
11015}
11016
11017/*
11018 * Read and execute commands. "Top" is nonzero for the top level command
11019 * loop; it turns on prompting if the shell is interactive.
11020 */
11021static int
11022cmdloop(int top)
11023{
11024 union node *n;
11025 struct stackmark smark;
11026 int inter;
11027 int numeof = 0;
11028
11029 TRACE(("cmdloop(%d) called\n", top));
11030 for (;;) {
11031 int skip;
11032
11033 setstackmark(&smark);
11034#if JOBS
11035 if (jobctl)
11036 showjobs(stderr, SHOW_CHANGED);
11037#endif
11038 inter = 0;
11039 if (iflag && top) {
11040 inter++;
11041#if ENABLE_ASH_MAIL
11042 chkmail();
11043#endif
11044 }
11045 n = parsecmd(inter);
11046 /* showtree(n); DEBUG */
11047 if (n == NEOF) {
11048 if (!top || numeof >= 50)
11049 break;
11050 if (!stoppedjobs()) {
11051 if (!Iflag)
11052 break;
11053 out2str("\nUse \"exit\" to leave shell.\n");
11054 }
11055 numeof++;
11056 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011057 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11058 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011059 numeof = 0;
11060 evaltree(n, 0);
11061 }
11062 popstackmark(&smark);
11063 skip = evalskip;
11064
11065 if (skip) {
11066 evalskip = 0;
11067 return skip & SKIPEVAL;
11068 }
11069 }
11070 return 0;
11071}
11072
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011073/*
11074 * Take commands from a file. To be compatible we should do a path
11075 * search for the file, which is necessary to find sub-commands.
11076 */
11077static char *
11078find_dot_file(char *name)
11079{
11080 char *fullname;
11081 const char *path = pathval();
11082 struct stat statb;
11083
11084 /* don't try this for absolute or relative paths */
11085 if (strchr(name, '/'))
11086 return name;
11087
11088 while ((fullname = padvance(&path, name)) != NULL) {
11089 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11090 /*
11091 * Don't bother freeing here, since it will
11092 * be freed by the caller.
11093 */
11094 return fullname;
11095 }
11096 stunalloc(fullname);
11097 }
11098
11099 /* not found in the PATH */
11100 ash_msg_and_raise_error("%s: not found", name);
11101 /* NOTREACHED */
11102}
11103
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011104static int
11105dotcmd(int argc, char **argv)
11106{
11107 struct strlist *sp;
11108 volatile struct shparam saveparam;
11109 int status = 0;
11110
11111 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011112 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011113
11114 if (argc >= 2) { /* That's what SVR2 does */
11115 char *fullname;
11116
11117 fullname = find_dot_file(argv[1]);
11118
11119 if (argc > 2) {
11120 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011121 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011122 shellparam.nparam = argc - 2;
11123 shellparam.p = argv + 2;
11124 };
11125
11126 setinputfile(fullname, INPUT_PUSH_FILE);
11127 commandname = fullname;
11128 cmdloop(0);
11129 popfile();
11130
11131 if (argc > 2) {
11132 freeparam(&shellparam);
11133 shellparam = saveparam;
11134 };
11135 status = exitstatus;
11136 }
11137 return status;
11138}
11139
11140static int
11141exitcmd(int argc, char **argv)
11142{
11143 if (stoppedjobs())
11144 return 0;
11145 if (argc > 1)
11146 exitstatus = number(argv[1]);
11147 raise_exception(EXEXIT);
11148 /* NOTREACHED */
11149}
11150
11151#if ENABLE_ASH_BUILTIN_ECHO
11152static int
11153echocmd(int argc, char **argv)
11154{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011155 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011156}
11157#endif
11158
11159#if ENABLE_ASH_BUILTIN_TEST
11160static int
11161testcmd(int argc, char **argv)
11162{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011163 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011164}
11165#endif
11166
11167/*
11168 * Read a file containing shell functions.
11169 */
11170static void
11171readcmdfile(char *name)
11172{
11173 setinputfile(name, INPUT_PUSH_FILE);
11174 cmdloop(0);
11175 popfile();
11176}
11177
11178
Denis Vlasenkocc571512007-02-23 21:10:35 +000011179/* ============ find_command inplementation */
11180
11181/*
11182 * Resolve a command name. If you change this routine, you may have to
11183 * change the shellexec routine as well.
11184 */
11185static void
11186find_command(char *name, struct cmdentry *entry, int act, const char *path)
11187{
11188 struct tblentry *cmdp;
11189 int idx;
11190 int prev;
11191 char *fullname;
11192 struct stat statb;
11193 int e;
11194 int updatetbl;
11195 struct builtincmd *bcmd;
11196
11197 /* If name contains a slash, don't use PATH or hash table */
11198 if (strchr(name, '/') != NULL) {
11199 entry->u.index = -1;
11200 if (act & DO_ABS) {
11201 while (stat(name, &statb) < 0) {
11202#ifdef SYSV
11203 if (errno == EINTR)
11204 continue;
11205#endif
11206 entry->cmdtype = CMDUNKNOWN;
11207 return;
11208 }
11209 }
11210 entry->cmdtype = CMDNORMAL;
11211 return;
11212 }
11213
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011214/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011215
11216 updatetbl = (path == pathval());
11217 if (!updatetbl) {
11218 act |= DO_ALTPATH;
11219 if (strstr(path, "%builtin") != NULL)
11220 act |= DO_ALTBLTIN;
11221 }
11222
11223 /* If name is in the table, check answer will be ok */
11224 cmdp = cmdlookup(name, 0);
11225 if (cmdp != NULL) {
11226 int bit;
11227
11228 switch (cmdp->cmdtype) {
11229 default:
11230#if DEBUG
11231 abort();
11232#endif
11233 case CMDNORMAL:
11234 bit = DO_ALTPATH;
11235 break;
11236 case CMDFUNCTION:
11237 bit = DO_NOFUNC;
11238 break;
11239 case CMDBUILTIN:
11240 bit = DO_ALTBLTIN;
11241 break;
11242 }
11243 if (act & bit) {
11244 updatetbl = 0;
11245 cmdp = NULL;
11246 } else if (cmdp->rehash == 0)
11247 /* if not invalidated by cd, we're done */
11248 goto success;
11249 }
11250
11251 /* If %builtin not in path, check for builtin next */
11252 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011253 if (bcmd) {
11254 if (IS_BUILTIN_REGULAR(bcmd))
11255 goto builtin_success;
11256 if (act & DO_ALTPATH) {
11257 if (!(act & DO_ALTBLTIN))
11258 goto builtin_success;
11259 } else if (builtinloc <= 0) {
11260 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011261 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011262 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011263
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011264#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011265 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011266 entry->cmdtype = CMDNORMAL;
11267 entry->u.index = -1;
11268 return;
11269 }
11270#endif
11271
Denis Vlasenkocc571512007-02-23 21:10:35 +000011272 /* We have to search path. */
11273 prev = -1; /* where to start */
11274 if (cmdp && cmdp->rehash) { /* doing a rehash */
11275 if (cmdp->cmdtype == CMDBUILTIN)
11276 prev = builtinloc;
11277 else
11278 prev = cmdp->param.index;
11279 }
11280
11281 e = ENOENT;
11282 idx = -1;
11283 loop:
11284 while ((fullname = padvance(&path, name)) != NULL) {
11285 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011286 /* NB: code below will still use fullname
11287 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011288 idx++;
11289 if (pathopt) {
11290 if (prefix(pathopt, "builtin")) {
11291 if (bcmd)
11292 goto builtin_success;
11293 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011294 } else if (!(act & DO_NOFUNC)
11295 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011296 /* handled below */
11297 } else {
11298 /* ignore unimplemented options */
11299 continue;
11300 }
11301 }
11302 /* if rehash, don't redo absolute path names */
11303 if (fullname[0] == '/' && idx <= prev) {
11304 if (idx < prev)
11305 continue;
11306 TRACE(("searchexec \"%s\": no change\n", name));
11307 goto success;
11308 }
11309 while (stat(fullname, &statb) < 0) {
11310#ifdef SYSV
11311 if (errno == EINTR)
11312 continue;
11313#endif
11314 if (errno != ENOENT && errno != ENOTDIR)
11315 e = errno;
11316 goto loop;
11317 }
11318 e = EACCES; /* if we fail, this will be the error */
11319 if (!S_ISREG(statb.st_mode))
11320 continue;
11321 if (pathopt) { /* this is a %func directory */
11322 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011323 /* NB: stalloc will return space pointed by fullname
11324 * (because we don't have any intervening allocations
11325 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011326 readcmdfile(fullname);
11327 cmdp = cmdlookup(name, 0);
11328 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11329 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11330 stunalloc(fullname);
11331 goto success;
11332 }
11333 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11334 if (!updatetbl) {
11335 entry->cmdtype = CMDNORMAL;
11336 entry->u.index = idx;
11337 return;
11338 }
11339 INT_OFF;
11340 cmdp = cmdlookup(name, 1);
11341 cmdp->cmdtype = CMDNORMAL;
11342 cmdp->param.index = idx;
11343 INT_ON;
11344 goto success;
11345 }
11346
11347 /* We failed. If there was an entry for this command, delete it */
11348 if (cmdp && updatetbl)
11349 delete_cmd_entry();
11350 if (act & DO_ERR)
11351 ash_msg("%s: %s", name, errmsg(e, "not found"));
11352 entry->cmdtype = CMDUNKNOWN;
11353 return;
11354
11355 builtin_success:
11356 if (!updatetbl) {
11357 entry->cmdtype = CMDBUILTIN;
11358 entry->u.cmd = bcmd;
11359 return;
11360 }
11361 INT_OFF;
11362 cmdp = cmdlookup(name, 1);
11363 cmdp->cmdtype = CMDBUILTIN;
11364 cmdp->param.cmd = bcmd;
11365 INT_ON;
11366 success:
11367 cmdp->rehash = 0;
11368 entry->cmdtype = cmdp->cmdtype;
11369 entry->u = cmdp->param;
11370}
11371
11372
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011373/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011374
Eric Andersencb57d552001-06-28 07:25:16 +000011375/*
Eric Andersencb57d552001-06-28 07:25:16 +000011376 * The trap builtin.
11377 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011378static int
Eric Andersenc470f442003-07-28 09:56:35 +000011379trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011380{
11381 char *action;
11382 char **ap;
11383 int signo;
11384
Eric Andersenc470f442003-07-28 09:56:35 +000011385 nextopt(nullstr);
11386 ap = argptr;
11387 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011388 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011389 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011390 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011391
Rob Landleyc9c1a412006-07-12 19:17:55 +000011392 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011393 out1fmt("trap -- %s %s\n",
11394 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011395 }
11396 }
11397 return 0;
11398 }
Eric Andersenc470f442003-07-28 09:56:35 +000011399 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011400 action = NULL;
11401 else
11402 action = *ap++;
11403 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011404 signo = get_signum(*ap);
11405 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011406 ash_msg_and_raise_error("%s: bad trap", *ap);
11407 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011408 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011409 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011410 action = NULL;
11411 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011412 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011413 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011414 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011415 trap[signo] = action;
11416 if (signo != 0)
11417 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011418 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011419 ap++;
11420 }
11421 return 0;
11422}
11423
Eric Andersenc470f442003-07-28 09:56:35 +000011424
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011425/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011426
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011427#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011428/*
11429 * Lists available builtins
11430 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011431static int
11432helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011433{
11434 int col, i;
11435
11436 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011437 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011438 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011439 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011440 if (col > 60) {
11441 out1fmt("\n");
11442 col = 0;
11443 }
11444 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011445#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011446 {
11447 const char *a = applet_names;
11448 while (*a) {
11449 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11450 if (col > 60) {
11451 out1fmt("\n");
11452 col = 0;
11453 }
11454 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011455 }
11456 }
11457#endif
11458 out1fmt("\n\n");
11459 return EXIT_SUCCESS;
11460}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011461#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011462
Eric Andersencb57d552001-06-28 07:25:16 +000011463/*
Eric Andersencb57d552001-06-28 07:25:16 +000011464 * The export and readonly commands.
11465 */
Eric Andersenc470f442003-07-28 09:56:35 +000011466static int
11467exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011468{
11469 struct var *vp;
11470 char *name;
11471 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011472 char **aptr;
11473 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011474
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011475 if (nextopt("p") != 'p') {
11476 aptr = argptr;
11477 name = *aptr;
11478 if (name) {
11479 do {
11480 p = strchr(name, '=');
11481 if (p != NULL) {
11482 p++;
11483 } else {
11484 vp = *findvar(hashvar(name), name);
11485 if (vp) {
11486 vp->flags |= flag;
11487 continue;
11488 }
Eric Andersencb57d552001-06-28 07:25:16 +000011489 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011490 setvar(name, p, flag);
11491 } while ((name = *++aptr) != NULL);
11492 return 0;
11493 }
Eric Andersencb57d552001-06-28 07:25:16 +000011494 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011495 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011496 return 0;
11497}
11498
Eric Andersencb57d552001-06-28 07:25:16 +000011499/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011500 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011501 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011502static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011503unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011504{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011505 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011506
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011507 cmdp = cmdlookup(name, 0);
11508 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11509 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011510}
11511
Eric Andersencb57d552001-06-28 07:25:16 +000011512/*
Eric Andersencb57d552001-06-28 07:25:16 +000011513 * The unset builtin command. We unset the function before we unset the
11514 * variable to allow a function to be unset when there is a readonly variable
11515 * with the same name.
11516 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011517static int
Eric Andersenc470f442003-07-28 09:56:35 +000011518unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011519{
11520 char **ap;
11521 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011522 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011523 int ret = 0;
11524
11525 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011526 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011527 }
Eric Andersencb57d552001-06-28 07:25:16 +000011528
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011529 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011530 if (flag != 'f') {
11531 i = unsetvar(*ap);
11532 ret |= i;
11533 if (!(i & 2))
11534 continue;
11535 }
11536 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011537 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011538 }
Eric Andersenc470f442003-07-28 09:56:35 +000011539 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011540}
11541
11542
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011543/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011544
Eric Andersenc470f442003-07-28 09:56:35 +000011545#include <sys/times.h>
11546
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011547static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011548 ' ', offsetof(struct tms, tms_utime),
11549 '\n', offsetof(struct tms, tms_stime),
11550 ' ', offsetof(struct tms, tms_cutime),
11551 '\n', offsetof(struct tms, tms_cstime),
11552 0
11553};
Eric Andersencb57d552001-06-28 07:25:16 +000011554
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011555static int
11556timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011557{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011558 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011559 const unsigned char *p;
11560 struct tms buf;
11561
11562 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011563 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011564
11565 p = timescmd_str;
11566 do {
11567 t = *(clock_t *)(((char *) &buf) + p[1]);
11568 s = t / clk_tck;
11569 out1fmt("%ldm%ld.%.3lds%c",
11570 s/60, s%60,
11571 ((t - s * clk_tck) * 1000) / clk_tck,
11572 p[0]);
11573 } while (*(p += 2));
11574
Eric Andersencb57d552001-06-28 07:25:16 +000011575 return 0;
11576}
11577
Denis Vlasenko131ae172007-02-18 13:00:19 +000011578#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011579static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011580dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011581{
Eric Andersened9ecf72004-06-22 08:29:45 +000011582 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011583 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011584
Denis Vlasenkob012b102007-02-19 22:43:01 +000011585 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011586 result = arith(s, &errcode);
11587 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011588 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011589 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011590 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011591 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011592 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011593 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011594 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011595 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011596 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011597
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011598 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011599}
Eric Andersenc470f442003-07-28 09:56:35 +000011600
Eric Andersenc470f442003-07-28 09:56:35 +000011601/*
Eric Andersen90898442003-08-06 11:20:52 +000011602 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11603 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11604 *
11605 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011606 */
11607static int
Eric Andersen90898442003-08-06 11:20:52 +000011608letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011609{
Eric Andersenc470f442003-07-28 09:56:35 +000011610 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011611 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011612
Eric Andersen90898442003-08-06 11:20:52 +000011613 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011614 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011615 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011616 for (ap = argv + 1; *ap; ap++) {
11617 i = dash_arith(*ap);
11618 }
Eric Andersenc470f442003-07-28 09:56:35 +000011619
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011620 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011621}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011622#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011623
Eric Andersenc470f442003-07-28 09:56:35 +000011624
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011625/* ============ miscbltin.c
11626 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011627 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011628 */
11629
11630#undef rflag
11631
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011632#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011633typedef enum __rlimit_resource rlim_t;
11634#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011635
Eric Andersenc470f442003-07-28 09:56:35 +000011636/*
11637 * The read builtin. The -e option causes backslashes to escape the
11638 * following character.
11639 *
11640 * This uses unbuffered input, which may be avoidable in some cases.
11641 */
Eric Andersenc470f442003-07-28 09:56:35 +000011642static int
11643readcmd(int argc, char **argv)
11644{
11645 char **ap;
11646 int backslash;
11647 char c;
11648 int rflag;
11649 char *prompt;
11650 const char *ifs;
11651 char *p;
11652 int startword;
11653 int status;
11654 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011655#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011656 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011657 int nchars = 0;
11658 int silent = 0;
11659 struct termios tty, old_tty;
11660#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011661#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011662 fd_set set;
11663 struct timeval ts;
11664
11665 ts.tv_sec = ts.tv_usec = 0;
11666#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011667
11668 rflag = 0;
11669 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011670#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011671 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011672#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011673 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011674#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011675 while ((i = nextopt("p:rt:")) != '\0')
11676#else
11677 while ((i = nextopt("p:r")) != '\0')
11678#endif
11679 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011680 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011681 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011682 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011683 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011684#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011685 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011686 nchars = bb_strtou(optionarg, NULL, 10);
11687 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011688 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011689 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011690 break;
11691 case 's':
11692 silent = 1;
11693 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011694#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011695#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011696 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011697 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011698 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011699 /* EINVAL means number is ok, but not terminated by NUL */
11700 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011701 char *p2;
11702 if (*++p) {
11703 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011704 ts.tv_usec = bb_strtou(p, &p2, 10);
11705 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011706 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011707 scale = p2 - p;
11708 /* normalize to usec */
11709 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011710 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011711 while (scale++ < 6)
11712 ts.tv_usec *= 10;
11713 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011714 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011715 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011716 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011717 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011718 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011719 }
Paul Fox02eb9342005-09-07 16:56:02 +000011720 break;
11721#endif
11722 case 'r':
11723 rflag = 1;
11724 break;
11725 default:
11726 break;
11727 }
Eric Andersenc470f442003-07-28 09:56:35 +000011728 }
11729 if (prompt && isatty(0)) {
11730 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011731 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011732 ap = argptr;
11733 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011734 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011735 ifs = bltinlookup("IFS");
11736 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011737 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011738#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011739 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011740 if (tcgetattr(0, &tty) != 0) {
11741 /* Not a tty */
11742 n_flag = 0;
11743 silent = 0;
11744 } else {
11745 old_tty = tty;
11746 if (n_flag) {
11747 tty.c_lflag &= ~ICANON;
11748 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11749 }
11750 if (silent) {
11751 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11752 }
11753 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011754 }
Paul Fox02eb9342005-09-07 16:56:02 +000011755 }
11756#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011757#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011758 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011759 FD_ZERO(&set);
11760 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011761
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011762 /* poll-based wait produces bigger code, using select */
11763 i = select(1, &set, NULL, NULL, &ts);
11764 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011765#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011766 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011767 tcsetattr(0, TCSANOW, &old_tty);
11768#endif
11769 return 1;
11770 }
11771 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011772#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011773 status = 0;
11774 startword = 1;
11775 backslash = 0;
11776 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011777 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011778 if (read(0, &c, 1) != 1) {
11779 status = 1;
11780 break;
11781 }
11782 if (c == '\0')
11783 continue;
11784 if (backslash) {
11785 backslash = 0;
11786 if (c != '\n')
11787 goto put;
11788 continue;
11789 }
11790 if (!rflag && c == '\\') {
11791 backslash++;
11792 continue;
11793 }
11794 if (c == '\n')
11795 break;
11796 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11797 continue;
11798 }
11799 startword = 0;
11800 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11801 STACKSTRNUL(p);
11802 setvar(*ap, stackblock(), 0);
11803 ap++;
11804 startword = 1;
11805 STARTSTACKSTR(p);
11806 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011807 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011808 STPUTC(c, p);
11809 }
11810 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011811/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011812#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011813 while (!n_flag || --nchars);
11814#else
11815 while (1);
11816#endif
11817
11818#if ENABLE_ASH_READ_NCHARS
11819 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011820 tcsetattr(0, TCSANOW, &old_tty);
11821#endif
11822
Eric Andersenc470f442003-07-28 09:56:35 +000011823 STACKSTRNUL(p);
11824 /* Remove trailing blanks */
11825 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11826 *p = '\0';
11827 setvar(*ap, stackblock(), 0);
11828 while (*++ap != NULL)
11829 setvar(*ap, nullstr, 0);
11830 return status;
11831}
11832
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011833static int
11834umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011835{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011836 static const char permuser[3] ALIGN1 = "ugo";
11837 static const char permmode[3] ALIGN1 = "rwx";
11838 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011839 S_IRUSR, S_IWUSR, S_IXUSR,
11840 S_IRGRP, S_IWGRP, S_IXGRP,
11841 S_IROTH, S_IWOTH, S_IXOTH
11842 };
11843
11844 char *ap;
11845 mode_t mask;
11846 int i;
11847 int symbolic_mode = 0;
11848
11849 while (nextopt("S") != '\0') {
11850 symbolic_mode = 1;
11851 }
11852
Denis Vlasenkob012b102007-02-19 22:43:01 +000011853 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011854 mask = umask(0);
11855 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011856 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011857
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011858 ap = *argptr;
11859 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011860 if (symbolic_mode) {
11861 char buf[18];
11862 char *p = buf;
11863
11864 for (i = 0; i < 3; i++) {
11865 int j;
11866
11867 *p++ = permuser[i];
11868 *p++ = '=';
11869 for (j = 0; j < 3; j++) {
11870 if ((mask & permmask[3 * i + j]) == 0) {
11871 *p++ = permmode[j];
11872 }
11873 }
11874 *p++ = ',';
11875 }
11876 *--p = 0;
11877 puts(buf);
11878 } else {
11879 out1fmt("%.4o\n", mask);
11880 }
11881 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011882 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011883 mask = 0;
11884 do {
11885 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011886 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011887 mask = (mask << 3) + (*ap - '0');
11888 } while (*++ap != '\0');
11889 umask(mask);
11890 } else {
11891 mask = ~mask & 0777;
11892 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011893 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011894 }
11895 umask(~mask & 0777);
11896 }
11897 }
11898 return 0;
11899}
11900
11901/*
11902 * ulimit builtin
11903 *
11904 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11905 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11906 * ash by J.T. Conklin.
11907 *
11908 * Public domain.
11909 */
11910
11911struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011912 uint8_t cmd; /* RLIMIT_xxx fit into it */
11913 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011914 char option;
11915};
11916
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011917static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011918#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011919 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011920#endif
11921#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011922 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011923#endif
11924#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011925 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011926#endif
11927#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011928 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011929#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011930#ifdef RLIMIT_CORE
11931 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011932#endif
11933#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011934 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011935#endif
11936#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011937 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011938#endif
11939#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011940 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011941#endif
11942#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011943 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011944#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011945#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011946 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011947#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011948#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011949 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011950#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011951};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011952static const char limits_name[] =
11953#ifdef RLIMIT_CPU
11954 "time(seconds)" "\0"
11955#endif
11956#ifdef RLIMIT_FSIZE
11957 "file(blocks)" "\0"
11958#endif
11959#ifdef RLIMIT_DATA
11960 "data(kb)" "\0"
11961#endif
11962#ifdef RLIMIT_STACK
11963 "stack(kb)" "\0"
11964#endif
11965#ifdef RLIMIT_CORE
11966 "coredump(blocks)" "\0"
11967#endif
11968#ifdef RLIMIT_RSS
11969 "memory(kb)" "\0"
11970#endif
11971#ifdef RLIMIT_MEMLOCK
11972 "locked memory(kb)" "\0"
11973#endif
11974#ifdef RLIMIT_NPROC
11975 "process" "\0"
11976#endif
11977#ifdef RLIMIT_NOFILE
11978 "nofiles" "\0"
11979#endif
11980#ifdef RLIMIT_AS
11981 "vmemory(kb)" "\0"
11982#endif
11983#ifdef RLIMIT_LOCKS
11984 "locks" "\0"
11985#endif
11986;
Eric Andersenc470f442003-07-28 09:56:35 +000011987
Glenn L McGrath76620622004-01-13 10:19:37 +000011988enum limtype { SOFT = 0x1, HARD = 0x2 };
11989
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011990static void
11991printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000011992 const struct limits *l)
11993{
11994 rlim_t val;
11995
11996 val = limit->rlim_max;
11997 if (how & SOFT)
11998 val = limit->rlim_cur;
11999
12000 if (val == RLIM_INFINITY)
12001 out1fmt("unlimited\n");
12002 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012003 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012004 out1fmt("%lld\n", (long long) val);
12005 }
12006}
12007
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012008static int
Eric Andersenc470f442003-07-28 09:56:35 +000012009ulimitcmd(int argc, char **argv)
12010{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012011 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012012 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012013 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012014 const struct limits *l;
12015 int set, all = 0;
12016 int optc, what;
12017 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012018
12019 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012020 while ((optc = nextopt("HSa"
12021#ifdef RLIMIT_CPU
12022 "t"
12023#endif
12024#ifdef RLIMIT_FSIZE
12025 "f"
12026#endif
12027#ifdef RLIMIT_DATA
12028 "d"
12029#endif
12030#ifdef RLIMIT_STACK
12031 "s"
12032#endif
12033#ifdef RLIMIT_CORE
12034 "c"
12035#endif
12036#ifdef RLIMIT_RSS
12037 "m"
12038#endif
12039#ifdef RLIMIT_MEMLOCK
12040 "l"
12041#endif
12042#ifdef RLIMIT_NPROC
12043 "p"
12044#endif
12045#ifdef RLIMIT_NOFILE
12046 "n"
12047#endif
12048#ifdef RLIMIT_AS
12049 "v"
12050#endif
12051#ifdef RLIMIT_LOCKS
12052 "w"
12053#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012054 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012055 switch (optc) {
12056 case 'H':
12057 how = HARD;
12058 break;
12059 case 'S':
12060 how = SOFT;
12061 break;
12062 case 'a':
12063 all = 1;
12064 break;
12065 default:
12066 what = optc;
12067 }
12068
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012069 for (l = limits_tbl; l->option != what; l++)
12070 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012071
12072 set = *argptr ? 1 : 0;
12073 if (set) {
12074 char *p = *argptr;
12075
12076 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012077 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012078 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012079 val = RLIM_INFINITY;
12080 else {
12081 val = (rlim_t) 0;
12082
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012083 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012084 val = (val * 10) + (long)(c - '0');
12085 if (val < (rlim_t) 0)
12086 break;
12087 }
12088 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012089 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012090 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012091 }
12092 }
12093 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012094 const char *lname = limits_name;
12095 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012096 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012097 out1fmt("%-20s ", lname);
12098 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012099 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012100 }
12101 return 0;
12102 }
12103
12104 getrlimit(l->cmd, &limit);
12105 if (set) {
12106 if (how & HARD)
12107 limit.rlim_max = val;
12108 if (how & SOFT)
12109 limit.rlim_cur = val;
12110 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012111 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012112 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012113 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012114 }
12115 return 0;
12116}
12117
Eric Andersen90898442003-08-06 11:20:52 +000012118
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012119/* ============ Math support */
12120
Denis Vlasenko131ae172007-02-18 13:00:19 +000012121#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012122
12123/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12124
12125 Permission is hereby granted, free of charge, to any person obtaining
12126 a copy of this software and associated documentation files (the
12127 "Software"), to deal in the Software without restriction, including
12128 without limitation the rights to use, copy, modify, merge, publish,
12129 distribute, sublicense, and/or sell copies of the Software, and to
12130 permit persons to whom the Software is furnished to do so, subject to
12131 the following conditions:
12132
12133 The above copyright notice and this permission notice shall be
12134 included in all copies or substantial portions of the Software.
12135
12136 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12137 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12138 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12139 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12140 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12141 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12142 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12143*/
12144
12145/* This is my infix parser/evaluator. It is optimized for size, intended
12146 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012147 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012148 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012149 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012150 * be that which POSIX specifies for shells. */
12151
12152/* The code uses a simple two-stack algorithm. See
12153 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012154 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012155 * this is based (this code differs in that it applies operators immediately
12156 * to the stack instead of adding them to a queue to end up with an
12157 * expression). */
12158
12159/* To use the routine, call it with an expression string and error return
12160 * pointer */
12161
12162/*
12163 * Aug 24, 2001 Manuel Novoa III
12164 *
12165 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12166 *
12167 * 1) In arith_apply():
12168 * a) Cached values of *numptr and &(numptr[-1]).
12169 * b) Removed redundant test for zero denominator.
12170 *
12171 * 2) In arith():
12172 * a) Eliminated redundant code for processing operator tokens by moving
12173 * to a table-based implementation. Also folded handling of parens
12174 * into the table.
12175 * b) Combined all 3 loops which called arith_apply to reduce generated
12176 * code size at the cost of speed.
12177 *
12178 * 3) The following expressions were treated as valid by the original code:
12179 * 1() , 0! , 1 ( *3 ) .
12180 * These bugs have been fixed by internally enclosing the expression in
12181 * parens and then checking that all binary ops and right parens are
12182 * preceded by a valid expression (NUM_TOKEN).
12183 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012184 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012185 * ctype's isspace() if it is used by another busybox applet or if additional
12186 * whitespace chars should be considered. Look below the "#include"s for a
12187 * precompiler test.
12188 */
12189
12190/*
12191 * Aug 26, 2001 Manuel Novoa III
12192 *
12193 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12194 *
12195 * Merge in Aaron's comments previously posted to the busybox list,
12196 * modified slightly to take account of my changes to the code.
12197 *
12198 */
12199
12200/*
12201 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12202 *
12203 * - allow access to variable,
12204 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12205 * - realize assign syntax (VAR=expr, +=, *= etc)
12206 * - realize exponentiation (** operator)
12207 * - realize comma separated - expr, expr
12208 * - realise ++expr --expr expr++ expr--
12209 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012210 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012211 * - was restored loses XOR operator
12212 * - remove one goto label, added three ;-)
12213 * - protect $((num num)) as true zero expr (Manuel`s error)
12214 * - always use special isspace(), see comment from bash ;-)
12215 */
12216
Eric Andersen90898442003-08-06 11:20:52 +000012217#define arith_isspace(arithval) \
12218 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12219
Eric Andersen90898442003-08-06 11:20:52 +000012220typedef unsigned char operator;
12221
12222/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012223 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012224 * precedence. The ID portion is so that multiple operators can have the
12225 * same precedence, ensuring that the leftmost one is evaluated first.
12226 * Consider * and /. */
12227
12228#define tok_decl(prec,id) (((id)<<5)|(prec))
12229#define PREC(op) ((op) & 0x1F)
12230
12231#define TOK_LPAREN tok_decl(0,0)
12232
12233#define TOK_COMMA tok_decl(1,0)
12234
12235#define TOK_ASSIGN tok_decl(2,0)
12236#define TOK_AND_ASSIGN tok_decl(2,1)
12237#define TOK_OR_ASSIGN tok_decl(2,2)
12238#define TOK_XOR_ASSIGN tok_decl(2,3)
12239#define TOK_PLUS_ASSIGN tok_decl(2,4)
12240#define TOK_MINUS_ASSIGN tok_decl(2,5)
12241#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12242#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12243
12244#define TOK_MUL_ASSIGN tok_decl(3,0)
12245#define TOK_DIV_ASSIGN tok_decl(3,1)
12246#define TOK_REM_ASSIGN tok_decl(3,2)
12247
12248/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012249#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012250
12251/* conditional is right associativity too */
12252#define TOK_CONDITIONAL tok_decl(4,0)
12253#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12254
12255#define TOK_OR tok_decl(5,0)
12256
12257#define TOK_AND tok_decl(6,0)
12258
12259#define TOK_BOR tok_decl(7,0)
12260
12261#define TOK_BXOR tok_decl(8,0)
12262
12263#define TOK_BAND tok_decl(9,0)
12264
12265#define TOK_EQ tok_decl(10,0)
12266#define TOK_NE tok_decl(10,1)
12267
12268#define TOK_LT tok_decl(11,0)
12269#define TOK_GT tok_decl(11,1)
12270#define TOK_GE tok_decl(11,2)
12271#define TOK_LE tok_decl(11,3)
12272
12273#define TOK_LSHIFT tok_decl(12,0)
12274#define TOK_RSHIFT tok_decl(12,1)
12275
12276#define TOK_ADD tok_decl(13,0)
12277#define TOK_SUB tok_decl(13,1)
12278
12279#define TOK_MUL tok_decl(14,0)
12280#define TOK_DIV tok_decl(14,1)
12281#define TOK_REM tok_decl(14,2)
12282
12283/* exponent is right associativity */
12284#define TOK_EXPONENT tok_decl(15,1)
12285
12286/* For now unary operators. */
12287#define UNARYPREC 16
12288#define TOK_BNOT tok_decl(UNARYPREC,0)
12289#define TOK_NOT tok_decl(UNARYPREC,1)
12290
12291#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12292#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12293
12294#define PREC_PRE (UNARYPREC+2)
12295
12296#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12297#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12298
12299#define PREC_POST (UNARYPREC+3)
12300
12301#define TOK_POST_INC tok_decl(PREC_POST, 0)
12302#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12303
12304#define SPEC_PREC (UNARYPREC+4)
12305
12306#define TOK_NUM tok_decl(SPEC_PREC, 0)
12307#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12308
12309#define NUMPTR (*numstackptr)
12310
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012311static int
12312tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012313{
12314 operator prec = PREC(op);
12315
12316 convert_prec_is_assing(prec);
12317 return (prec == PREC(TOK_ASSIGN) ||
12318 prec == PREC_PRE || prec == PREC_POST);
12319}
12320
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012321static int
12322is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012323{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012324 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12325 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012326}
12327
Eric Andersen90898442003-08-06 11:20:52 +000012328typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012329 arith_t val;
12330 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012331 char contidional_second_val_initialized;
12332 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012333 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012334} v_n_t;
12335
Eric Andersen90898442003-08-06 11:20:52 +000012336typedef struct CHK_VAR_RECURSIVE_LOOPED {
12337 const char *var;
12338 struct CHK_VAR_RECURSIVE_LOOPED *next;
12339} chk_var_recursive_looped_t;
12340
12341static chk_var_recursive_looped_t *prev_chk_var_recursive;
12342
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012343static int
12344arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012345{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012346 if (t->var) {
12347 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012349 if (p) {
12350 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012351
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012352 /* recursive try as expression */
12353 chk_var_recursive_looped_t *cur;
12354 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012355
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012356 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12357 if (strcmp(cur->var, t->var) == 0) {
12358 /* expression recursion loop detected */
12359 return -5;
12360 }
12361 }
12362 /* save current lookuped var name */
12363 cur = prev_chk_var_recursive;
12364 cur_save.var = t->var;
12365 cur_save.next = cur;
12366 prev_chk_var_recursive = &cur_save;
12367
12368 t->val = arith (p, &errcode);
12369 /* restore previous ptr after recursiving */
12370 prev_chk_var_recursive = cur;
12371 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012372 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012373 /* allow undefined var as 0 */
12374 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012375 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012376 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012377}
12378
12379/* "applying" a token means performing it on the top elements on the integer
12380 * stack. For a unary operator it will only change the top element, but a
12381 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012382static int
12383arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012384{
Eric Andersen90898442003-08-06 11:20:52 +000012385 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012386 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012387 int ret_arith_lookup_val;
12388
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012389 /* There is no operator that can work without arguments */
12390 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012391 numptr_m1 = NUMPTR - 1;
12392
12393 /* check operand is var with noninteger value */
12394 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012395 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012396 return ret_arith_lookup_val;
12397
12398 rez = numptr_m1->val;
12399 if (op == TOK_UMINUS)
12400 rez *= -1;
12401 else if (op == TOK_NOT)
12402 rez = !rez;
12403 else if (op == TOK_BNOT)
12404 rez = ~rez;
12405 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12406 rez++;
12407 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12408 rez--;
12409 else if (op != TOK_UPLUS) {
12410 /* Binary operators */
12411
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012412 /* check and binary operators need two arguments */
12413 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012414
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012415 /* ... and they pop one */
12416 --NUMPTR;
12417 numptr_val = rez;
12418 if (op == TOK_CONDITIONAL) {
12419 if (! numptr_m1->contidional_second_val_initialized) {
12420 /* protect $((expr1 ? expr2)) without ": expr" */
12421 goto err;
12422 }
12423 rez = numptr_m1->contidional_second_val;
12424 } else if (numptr_m1->contidional_second_val_initialized) {
12425 /* protect $((expr1 : expr2)) without "expr ? " */
12426 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012427 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012428 numptr_m1 = NUMPTR - 1;
12429 if (op != TOK_ASSIGN) {
12430 /* check operand is var with noninteger value for not '=' */
12431 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12432 if (ret_arith_lookup_val)
12433 return ret_arith_lookup_val;
12434 }
12435 if (op == TOK_CONDITIONAL) {
12436 numptr_m1->contidional_second_val = rez;
12437 }
12438 rez = numptr_m1->val;
12439 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012440 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012441 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012442 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012443 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012444 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012445 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012446 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012447 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012448 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012449 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012450 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012452 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012454 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012456 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012457 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012458 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012459 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012460 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012462 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012463 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012464 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012465 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012466 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012467 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012468 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012469 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012470 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012471 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012472 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012473 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012474 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012475 /* protect $((expr : expr)) without "expr ? " */
12476 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012477 }
12478 numptr_m1->contidional_second_val_initialized = op;
12479 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012480 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012481 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012482 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012483 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012484 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012485 return -3; /* exponent less than 0 */
12486 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012487 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012488
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012489 if (numptr_val)
12490 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012491 c *= rez;
12492 rez = c;
12493 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012494 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012495 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012496 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012497 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012498 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012499 rez %= numptr_val;
12500 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012501 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012502 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012503
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012504 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012505 /* Hmm, 1=2 ? */
12506 goto err;
12507 }
12508 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012509#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012510 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012511#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012512 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012513#endif
Eric Andersen90898442003-08-06 11:20:52 +000012514 setvar(numptr_m1->var, buf, 0);
12515 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012516 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012517 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012518 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012519 rez++;
12520 }
12521 numptr_m1->val = rez;
12522 /* protect geting var value, is number now */
12523 numptr_m1->var = NULL;
12524 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012525 err:
12526 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012527}
12528
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012529/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012530static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012531 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12532 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12533 '<','<', 0, TOK_LSHIFT,
12534 '>','>', 0, TOK_RSHIFT,
12535 '|','|', 0, TOK_OR,
12536 '&','&', 0, TOK_AND,
12537 '!','=', 0, TOK_NE,
12538 '<','=', 0, TOK_LE,
12539 '>','=', 0, TOK_GE,
12540 '=','=', 0, TOK_EQ,
12541 '|','=', 0, TOK_OR_ASSIGN,
12542 '&','=', 0, TOK_AND_ASSIGN,
12543 '*','=', 0, TOK_MUL_ASSIGN,
12544 '/','=', 0, TOK_DIV_ASSIGN,
12545 '%','=', 0, TOK_REM_ASSIGN,
12546 '+','=', 0, TOK_PLUS_ASSIGN,
12547 '-','=', 0, TOK_MINUS_ASSIGN,
12548 '-','-', 0, TOK_POST_DEC,
12549 '^','=', 0, TOK_XOR_ASSIGN,
12550 '+','+', 0, TOK_POST_INC,
12551 '*','*', 0, TOK_EXPONENT,
12552 '!', 0, TOK_NOT,
12553 '<', 0, TOK_LT,
12554 '>', 0, TOK_GT,
12555 '=', 0, TOK_ASSIGN,
12556 '|', 0, TOK_BOR,
12557 '&', 0, TOK_BAND,
12558 '*', 0, TOK_MUL,
12559 '/', 0, TOK_DIV,
12560 '%', 0, TOK_REM,
12561 '+', 0, TOK_ADD,
12562 '-', 0, TOK_SUB,
12563 '^', 0, TOK_BXOR,
12564 /* uniq */
12565 '~', 0, TOK_BNOT,
12566 ',', 0, TOK_COMMA,
12567 '?', 0, TOK_CONDITIONAL,
12568 ':', 0, TOK_CONDITIONAL_SEP,
12569 ')', 0, TOK_RPAREN,
12570 '(', 0, TOK_LPAREN,
12571 0
12572};
12573/* ptr to ")" */
12574#define endexpression &op_tokens[sizeof(op_tokens)-7]
12575
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012576static arith_t
12577arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012578{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012579 char arithval; /* Current character under analysis */
12580 operator lasttok, op;
12581 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012582
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012583 const char *p = endexpression;
12584 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012585
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012586 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012587
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012588 /* Stack of integers */
12589 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12590 * in any given correct or incorrect expression is left as an exercise to
12591 * the reader. */
12592 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12593 *numstackptr = numstack;
12594 /* Stack of operator tokens */
12595 operator *stack = alloca((datasizes) * sizeof(operator)),
12596 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012597
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012598 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12599 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012600
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012601 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012602 arithval = *expr;
12603 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012604 if (p == endexpression) {
12605 /* Null expression. */
12606 return 0;
12607 }
12608
12609 /* This is only reached after all tokens have been extracted from the
12610 * input stream. If there are still tokens on the operator stack, they
12611 * are to be applied in order. At the end, there should be a final
12612 * result on the integer stack */
12613
12614 if (expr != endexpression + 1) {
12615 /* If we haven't done so already, */
12616 /* append a closing right paren */
12617 expr = endexpression;
12618 /* and let the loop process it. */
12619 continue;
12620 }
12621 /* At this point, we're done with the expression. */
12622 if (numstackptr != numstack+1) {
12623 /* ... but if there isn't, it's bad */
12624 err:
12625 return (*perrcode = -1);
12626 }
12627 if (numstack->var) {
12628 /* expression is $((var)) only, lookup now */
12629 errcode = arith_lookup_val(numstack);
12630 }
12631 ret:
12632 *perrcode = errcode;
12633 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012634 }
12635
Eric Andersen90898442003-08-06 11:20:52 +000012636 /* Continue processing the expression. */
12637 if (arith_isspace(arithval)) {
12638 /* Skip whitespace */
12639 goto prologue;
12640 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012641 p = endofname(expr);
12642 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012643 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012644
12645 numstackptr->var = alloca(var_name_size);
12646 safe_strncpy(numstackptr->var, expr, var_name_size);
12647 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012648 num:
Eric Andersen90898442003-08-06 11:20:52 +000012649 numstackptr->contidional_second_val_initialized = 0;
12650 numstackptr++;
12651 lasttok = TOK_NUM;
12652 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012653 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012654 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012655 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012656#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012657 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012658#else
12659 numstackptr->val = strtol(expr, (char **) &expr, 0);
12660#endif
Eric Andersen90898442003-08-06 11:20:52 +000012661 goto num;
12662 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012663 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012664 const char *o;
12665
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012666 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012667 /* strange operator not found */
12668 goto err;
12669 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012670 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012671 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012672 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012673 /* found */
12674 expr = o - 1;
12675 break;
12676 }
12677 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012678 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012679 p++;
12680 /* skip zero delim */
12681 p++;
12682 }
12683 op = p[1];
12684
12685 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012686 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12687 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012688
12689 /* Plus and minus are binary (not unary) _only_ if the last
12690 * token was as number, or a right paren (which pretends to be
12691 * a number, since it evaluates to one). Think about it.
12692 * It makes sense. */
12693 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012694 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012695 case TOK_ADD:
12696 op = TOK_UPLUS;
12697 break;
12698 case TOK_SUB:
12699 op = TOK_UMINUS;
12700 break;
12701 case TOK_POST_INC:
12702 op = TOK_PRE_INC;
12703 break;
12704 case TOK_POST_DEC:
12705 op = TOK_PRE_DEC;
12706 break;
Eric Andersen90898442003-08-06 11:20:52 +000012707 }
12708 }
12709 /* We don't want a unary operator to cause recursive descent on the
12710 * stack, because there can be many in a row and it could cause an
12711 * operator to be evaluated before its argument is pushed onto the
12712 * integer stack. */
12713 /* But for binary operators, "apply" everything on the operator
12714 * stack until we find an operator with a lesser priority than the
12715 * one we have just extracted. */
12716 /* Left paren is given the lowest priority so it will never be
12717 * "applied" in this way.
12718 * if associativity is right and priority eq, applied also skip
12719 */
12720 prec = PREC(op);
12721 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12722 /* not left paren or unary */
12723 if (lasttok != TOK_NUM) {
12724 /* binary op must be preceded by a num */
12725 goto err;
12726 }
12727 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012728 if (op == TOK_RPAREN) {
12729 /* The algorithm employed here is simple: while we don't
12730 * hit an open paren nor the bottom of the stack, pop
12731 * tokens and apply them */
12732 if (stackptr[-1] == TOK_LPAREN) {
12733 --stackptr;
12734 /* Any operator directly after a */
12735 lasttok = TOK_NUM;
12736 /* close paren should consider itself binary */
12737 goto prologue;
12738 }
12739 } else {
12740 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012741
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012742 convert_prec_is_assing(prec);
12743 convert_prec_is_assing(prev_prec);
12744 if (prev_prec < prec)
12745 break;
12746 /* check right assoc */
12747 if (prev_prec == prec && is_right_associativity(prec))
12748 break;
12749 }
12750 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12751 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012752 }
12753 if (op == TOK_RPAREN) {
12754 goto err;
12755 }
12756 }
12757
12758 /* Push this operator to the stack and remember it. */
12759 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012760 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012761 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012762 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012763}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012764#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012765
12766
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012767/* ============ main() and helpers */
12768
12769/*
12770 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012771 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012772static void exitshell(void) ATTRIBUTE_NORETURN;
12773static void
12774exitshell(void)
12775{
12776 struct jmploc loc;
12777 char *p;
12778 int status;
12779
12780 status = exitstatus;
12781 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12782 if (setjmp(loc.loc)) {
12783 if (exception == EXEXIT)
12784/* dash bug: it just does _exit(exitstatus) here
12785 * but we have to do setjobctl(0) first!
12786 * (bug is still not fixed in dash-0.5.3 - if you run dash
12787 * under Midnight Commander, on exit from dash MC is backgrounded) */
12788 status = exitstatus;
12789 goto out;
12790 }
12791 exception_handler = &loc;
12792 p = trap[0];
12793 if (p) {
12794 trap[0] = NULL;
12795 evalstring(p, 0);
12796 }
12797 flush_stdout_stderr();
12798 out:
12799 setjobctl(0);
12800 _exit(status);
12801 /* NOTREACHED */
12802}
12803
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012804static void
12805init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012806{
12807 /* from input.c: */
12808 basepf.nextc = basepf.buf = basebuf;
12809
12810 /* from trap.c: */
12811 signal(SIGCHLD, SIG_DFL);
12812
12813 /* from var.c: */
12814 {
12815 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012816 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012817 const char *p;
12818 struct stat st1, st2;
12819
12820 initvar();
12821 for (envp = environ; envp && *envp; envp++) {
12822 if (strchr(*envp, '=')) {
12823 setvareq(*envp, VEXPORT|VTEXTFIXED);
12824 }
12825 }
12826
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012827 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012828 setvar("PPID", ppid, 0);
12829
12830 p = lookupvar("PWD");
12831 if (p)
12832 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12833 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12834 p = '\0';
12835 setpwd(p, 0);
12836 }
12837}
12838
12839/*
12840 * Process the shell command line arguments.
12841 */
12842static void
12843procargs(int argc, char **argv)
12844{
12845 int i;
12846 const char *xminusc;
12847 char **xargv;
12848
12849 xargv = argv;
12850 arg0 = xargv[0];
12851 if (argc > 0)
12852 xargv++;
12853 for (i = 0; i < NOPTS; i++)
12854 optlist[i] = 2;
12855 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012856 if (options(1)) {
12857 /* it already printed err message */
12858 raise_exception(EXERROR);
12859 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012860 xargv = argptr;
12861 xminusc = minusc;
12862 if (*xargv == NULL) {
12863 if (xminusc)
12864 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12865 sflag = 1;
12866 }
12867 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12868 iflag = 1;
12869 if (mflag == 2)
12870 mflag = iflag;
12871 for (i = 0; i < NOPTS; i++)
12872 if (optlist[i] == 2)
12873 optlist[i] = 0;
12874#if DEBUG == 2
12875 debug = 1;
12876#endif
12877 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12878 if (xminusc) {
12879 minusc = *xargv++;
12880 if (*xargv)
12881 goto setarg0;
12882 } else if (!sflag) {
12883 setinputfile(*xargv, 0);
12884 setarg0:
12885 arg0 = *xargv++;
12886 commandname = arg0;
12887 }
12888
12889 shellparam.p = xargv;
12890#if ENABLE_ASH_GETOPTS
12891 shellparam.optind = 1;
12892 shellparam.optoff = -1;
12893#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012894 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012895 while (*xargv) {
12896 shellparam.nparam++;
12897 xargv++;
12898 }
12899 optschanged();
12900}
12901
12902/*
12903 * Read /etc/profile or .profile.
12904 */
12905static void
12906read_profile(const char *name)
12907{
12908 int skip;
12909
12910 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12911 return;
12912 skip = cmdloop(0);
12913 popfile();
12914 if (skip)
12915 exitshell();
12916}
12917
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012918/*
12919 * This routine is called when an error or an interrupt occurs in an
12920 * interactive shell and control is returned to the main command loop.
12921 */
12922static void
12923reset(void)
12924{
12925 /* from eval.c: */
12926 evalskip = 0;
12927 loopnest = 0;
12928 /* from input.c: */
12929 parselleft = parsenleft = 0; /* clear input buffer */
12930 popallfiles();
12931 /* from parser.c: */
12932 tokpushback = 0;
12933 checkkwd = 0;
12934 /* from redir.c: */
12935 clearredir(0);
12936}
12937
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012938#if PROFILE
12939static short profile_buf[16384];
12940extern int etext();
12941#endif
12942
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012943/*
12944 * Main routine. We initialize things, parse the arguments, execute
12945 * profiles if we're a login shell, and then call cmdloop to execute
12946 * commands. The setjmp call sets up the location to jump to when an
12947 * exception occurs. When an exception occurs the variable "state"
12948 * is used to figure out how far we had gotten.
12949 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012950int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012951int ash_main(int argc, char **argv)
12952{
12953 char *shinit;
12954 volatile int state;
12955 struct jmploc jmploc;
12956 struct stackmark smark;
12957
Denis Vlasenko01631112007-12-16 17:20:38 +000012958 /* Initialize global data */
12959 INIT_G_misc();
12960 INIT_G_memstack();
12961 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012962#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012963 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012964#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012965 INIT_G_cmdtable();
12966
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012967#if PROFILE
12968 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12969#endif
12970
12971#if ENABLE_FEATURE_EDITING
12972 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12973#endif
12974 state = 0;
12975 if (setjmp(jmploc.loc)) {
12976 int e;
12977 int s;
12978
12979 reset();
12980
12981 e = exception;
12982 if (e == EXERROR)
12983 exitstatus = 2;
12984 s = state;
12985 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12986 exitshell();
12987
12988 if (e == EXINT) {
12989 outcslow('\n', stderr);
12990 }
12991 popstackmark(&smark);
12992 FORCE_INT_ON; /* enable interrupts */
12993 if (s == 1)
12994 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012995 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012996 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012997 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012998 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000012999 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013000 }
13001 exception_handler = &jmploc;
13002#if DEBUG
13003 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013004 trace_puts("Shell args: ");
13005 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006#endif
13007 rootpid = getpid();
13008
13009#if ENABLE_ASH_RANDOM_SUPPORT
13010 rseed = rootpid + time(NULL);
13011#endif
13012 init();
13013 setstackmark(&smark);
13014 procargs(argc, argv);
13015#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13016 if (iflag) {
13017 const char *hp = lookupvar("HISTFILE");
13018
13019 if (hp == NULL) {
13020 hp = lookupvar("HOME");
13021 if (hp != NULL) {
13022 char *defhp = concat_path_file(hp, ".ash_history");
13023 setvar("HISTFILE", defhp, 0);
13024 free(defhp);
13025 }
13026 }
13027 }
13028#endif
13029 if (argv[0] && argv[0][0] == '-')
13030 isloginsh = 1;
13031 if (isloginsh) {
13032 state = 1;
13033 read_profile("/etc/profile");
13034 state1:
13035 state = 2;
13036 read_profile(".profile");
13037 }
13038 state2:
13039 state = 3;
13040 if (
13041#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013042 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013043#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013044 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013045 ) {
13046 shinit = lookupvar("ENV");
13047 if (shinit != NULL && *shinit != '\0') {
13048 read_profile(shinit);
13049 }
13050 }
13051 state3:
13052 state = 4;
13053 if (minusc)
13054 evalstring(minusc, 0);
13055
13056 if (sflag || minusc == NULL) {
13057#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13058 if ( iflag ) {
13059 const char *hp = lookupvar("HISTFILE");
13060
13061 if (hp != NULL)
13062 line_input_state->hist_file = hp;
13063 }
13064#endif
13065 state4: /* XXX ??? - why isn't this before the "if" statement */
13066 cmdloop(1);
13067 }
13068#if PROFILE
13069 monitor(0);
13070#endif
13071#ifdef GPROF
13072 {
13073 extern void _mcleanup(void);
13074 _mcleanup();
13075 }
13076#endif
13077 exitshell();
13078 /* NOTREACHED */
13079}
13080
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013081#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013082const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013083int main(int argc, char **argv)
13084{
13085 return ash_main(argc, argv);
13086}
13087#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013088
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013089
Eric Andersendf82f612001-06-28 07:46:40 +000013090/*-
13091 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013092 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013093 *
13094 * This code is derived from software contributed to Berkeley by
13095 * Kenneth Almquist.
13096 *
13097 * Redistribution and use in source and binary forms, with or without
13098 * modification, are permitted provided that the following conditions
13099 * are met:
13100 * 1. Redistributions of source code must retain the above copyright
13101 * notice, this list of conditions and the following disclaimer.
13102 * 2. Redistributions in binary form must reproduce the above copyright
13103 * notice, this list of conditions and the following disclaimer in the
13104 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013105 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013106 * may be used to endorse or promote products derived from this software
13107 * without specific prior written permission.
13108 *
13109 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13110 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13111 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13112 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13113 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13114 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13115 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13116 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13117 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13118 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13119 * SUCH DAMAGE.
13120 */