blob: 25c8fa521396c2245d5ce37decb44ab7929db96f [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +00006 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +00007 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +00008 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +00009 * was re-ported from NetBSD and debianized.
10 *
11 *
Eric Andersencb57d552001-06-28 07:25:16 +000012 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000015 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000016 *
Eric Andersen81fe1232003-07-29 06:38:40 +000017 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000018 */
19
Eric Andersenc470f442003-07-28 09:56:35 +000020/*
Eric Andersen90898442003-08-06 11:20:52 +000021 * rewrite arith.y to micro stack based cryptic algorithm by
22 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
23 *
Eric Andersenef02f822004-03-11 13:34:24 +000024 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
25 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000026 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000027 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000028 * used in busybox and size optimizations,
29 * rewrote arith (see notes to this), added locale support,
30 * rewrote dynamic variables.
31 *
Eric Andersen90898442003-08-06 11:20:52 +000032 */
33
Eric Andersen90898442003-08-06 11:20:52 +000034/*
Eric Andersenc470f442003-07-28 09:56:35 +000035 * The follow should be set to reflect the type of system you have:
36 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
37 * define SYSV if you are running under System V.
38 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
39 * define DEBUG=2 to compile in and turn on debugging.
40 *
41 * When debugging is on, debugging info will be written to ./trace and
42 * a quit signal will generate a core dump.
43 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000044#define DEBUG 0
Eric Andersenc470f442003-07-28 09:56:35 +000045#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000046
47#define IFS_BROKEN
48
49#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000050
Denis Vlasenkob012b102007-02-19 22:43:01 +000051#if DEBUG
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000052#ifndef _GNU_SOURCE
Denis Vlasenkob012b102007-02-19 22:43:01 +000053#define _GNU_SOURCE
54#endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000055#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000056
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000057#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000058#include <paths.h>
59#include <setjmp.h>
60#include <fnmatch.h>
Denis Vlasenko131ae172007-02-18 13:00:19 +000061#if JOBS || ENABLE_ASH_READ_NCHARS
Eric Andersencb57d552001-06-28 07:25:16 +000062#include <termios.h>
Eric Andersencb57d552001-06-28 07:25:16 +000063#endif
64
Denis Vlasenkob012b102007-02-19 22:43:01 +000065#if defined(__uClinux__)
66#error "Do not even bother, ash will not run on uClinux"
67#endif
68
Denis Vlasenkob012b102007-02-19 22:43:01 +000069
Denis Vlasenko01631112007-12-16 17:20:38 +000070/* ============ Hash table sizes. Configurable. */
71
72#define VTABSIZE 39
73#define ATABSIZE 39
74#define CMDTABLESIZE 31 /* should be prime */
75
76
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000077/* ============ Misc helpers */
78
79#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
80
81/* C99 say: "char" declaration may be signed or unsigned default */
82#define signed_char2int(sc) ((int)((signed char)sc))
83
84
Denis Vlasenkob012b102007-02-19 22:43:01 +000085/* ============ Shell options */
86
87static const char *const optletters_optnames[] = {
88 "e" "errexit",
89 "f" "noglob",
90 "I" "ignoreeof",
91 "i" "interactive",
92 "m" "monitor",
93 "n" "noexec",
94 "s" "stdin",
95 "x" "xtrace",
96 "v" "verbose",
97 "C" "noclobber",
98 "a" "allexport",
99 "b" "notify",
100 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000101 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000102#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000103 ,"\0" "nolog"
104 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000105#endif
106};
107
108#define optletters(n) optletters_optnames[(n)][0]
109#define optnames(n) (&optletters_optnames[(n)][1])
110
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000111enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000112
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000113static char optlist[NOPTS] ALIGN1;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114
115#define eflag optlist[0]
116#define fflag optlist[1]
117#define Iflag optlist[2]
118#define iflag optlist[3]
119#define mflag optlist[4]
120#define nflag optlist[5]
121#define sflag optlist[6]
122#define xflag optlist[7]
123#define vflag optlist[8]
124#define Cflag optlist[9]
125#define aflag optlist[10]
126#define bflag optlist[11]
127#define uflag optlist[12]
128#define viflag optlist[13]
129#if DEBUG
130#define nolog optlist[14]
131#define debug optlist[15]
132#endif
Eric Andersenc470f442003-07-28 09:56:35 +0000133
134
Denis Vlasenkob012b102007-02-19 22:43:01 +0000135/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000136
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000137static const char homestr[] ALIGN1 = "HOME";
138static const char snlfmt[] ALIGN1 = "%s\n";
139static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000140
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000141/*
Eric Andersenc470f442003-07-28 09:56:35 +0000142 * We enclose jmp_buf in a structure so that we can declare pointers to
143 * jump locations. The global variable handler contains the location to
144 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000145 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000146 * exception handlers, the user should save the value of handler on entry
147 * to an inner scope, set handler to point to a jmploc structure for the
148 * inner scope, and restore handler on exit from the scope.
149 */
Eric Andersenc470f442003-07-28 09:56:35 +0000150struct jmploc {
151 jmp_buf loc;
152};
Denis Vlasenko01631112007-12-16 17:20:38 +0000153
154struct globals_misc {
155 /* pid of main shell */
156 int rootpid;
157 /* shell level: 0 for the main shell, 1 for its children, and so on */
158 int shlvl;
159#define rootshell (!shlvl)
160 char *minusc; /* argument to -c option */
161
162 char *curdir; // = nullstr; /* current working directory */
163 char *physdir; // = nullstr; /* physical working directory */
164
165 char *arg0; /* value of $0 */
166
167 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000168
169// disabled by vda: cannot understand how it was supposed to work -
170// cannot fix bugs. That's why you have to explain your non-trivial designs!
171// /* do we generate EXSIG events */
172// int exsig; /* counter */
173 volatile int suppressint; /* counter */
174 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
175 /* last pending signal */
176 volatile /*sig_atomic_t*/ smallint pendingsig;
177 smallint exception; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000178 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000179#define EXINT 0 /* SIGINT received */
180#define EXERROR 1 /* a generic error */
181#define EXSHELLPROC 2 /* execute a shell procedure */
182#define EXEXEC 3 /* command execution failed */
183#define EXEXIT 4 /* exit the shell */
184#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000185
Denis Vlasenko01631112007-12-16 17:20:38 +0000186 /* trap handler commands */
187 char *trap[NSIG];
188 smallint isloginsh;
189 char nullstr[1]; /* zero length string */
190 /*
191 * Sigmode records the current value of the signal handlers for the various
192 * modes. A value of zero means that the current handler is not known.
193 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
194 */
195 char sigmode[NSIG - 1];
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000196#define S_DFL 1 /* default signal handling (SIG_DFL) */
197#define S_CATCH 2 /* signal is caught */
198#define S_IGN 3 /* signal is ignored (SIG_IGN) */
199#define S_HARD_IGN 4 /* signal is ignored permenantly */
200#define S_RESET 5 /* temporary - to reset a hard ignored sig */
201
Denis Vlasenko01631112007-12-16 17:20:38 +0000202 /* indicates specified signal received */
203 char gotsig[NSIG - 1];
204};
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
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001788#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001789#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001790# define vmail (&vifs)[1]
1791# define vmpath (&vmail)[1]
1792# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001794# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001795#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001796#define vps1 (&vpath)[1]
1797#define vps2 (&vps1)[1]
1798#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001799#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001800# define voptind (&vps4)[1]
1801# if ENABLE_ASH_RANDOM_SUPPORT
1802# define vrandom (&voptind)[1]
1803# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001804#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001805# if ENABLE_ASH_RANDOM_SUPPORT
1806# define vrandom (&vps4)[1]
1807# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001808#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001809
1810/*
1811 * The following macros access the values of the above variables.
1812 * They have to skip over the name. They return the null string
1813 * for unset variables.
1814 */
1815#define ifsval() (vifs.text + 4)
1816#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001817#if ENABLE_ASH_MAIL
1818# define mailval() (vmail.text + 5)
1819# define mpathval() (vmpath.text + 9)
1820# define mpathset() ((vmpath.flags & VUNSET) == 0)
1821#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001822#define pathval() (vpath.text + 5)
1823#define ps1val() (vps1.text + 4)
1824#define ps2val() (vps2.text + 4)
1825#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001826#if ENABLE_ASH_GETOPTS
1827# define optindval() (voptind.text + 7)
1828#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001829
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001831#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1832#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1833
Denis Vlasenko01631112007-12-16 17:20:38 +00001834#if ENABLE_ASH_GETOPTS
1835static void
1836getoptsreset(const char *value)
1837{
1838 shellparam.optind = number(value);
1839 shellparam.optoff = -1;
1840}
1841#endif
1842
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001843/*
1844 * Return of a legal variable name (a letter or underscore followed by zero or
1845 * more letters, underscores, and digits).
1846 */
1847static char *
1848endofname(const char *name)
1849{
1850 char *p;
1851
1852 p = (char *) name;
1853 if (!is_name(*p))
1854 return p;
1855 while (*++p) {
1856 if (!is_in_name(*p))
1857 break;
1858 }
1859 return p;
1860}
1861
1862/*
1863 * Compares two strings up to the first = or '\0'. The first
1864 * string must be terminated by '='; the second may be terminated by
1865 * either '=' or '\0'.
1866 */
1867static int
1868varcmp(const char *p, const char *q)
1869{
1870 int c, d;
1871
1872 while ((c = *p) == (d = *q)) {
1873 if (!c || c == '=')
1874 goto out;
1875 p++;
1876 q++;
1877 }
1878 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001879 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001880 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001881 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001882 out:
1883 return c - d;
1884}
1885
1886static int
1887varequal(const char *a, const char *b)
1888{
1889 return !varcmp(a, b);
1890}
1891
1892/*
1893 * Find the appropriate entry in the hash table from the name.
1894 */
1895static struct var **
1896hashvar(const char *p)
1897{
1898 unsigned hashval;
1899
1900 hashval = ((unsigned char) *p) << 4;
1901 while (*p && *p != '=')
1902 hashval += (unsigned char) *p++;
1903 return &vartab[hashval % VTABSIZE];
1904}
1905
1906static int
1907vpcmp(const void *a, const void *b)
1908{
1909 return varcmp(*(const char **)a, *(const char **)b);
1910}
1911
1912/*
1913 * This routine initializes the builtin variables.
1914 */
1915static void
1916initvar(void)
1917{
1918 struct var *vp;
1919 struct var *end;
1920 struct var **vpp;
1921
1922 /*
1923 * PS1 depends on uid
1924 */
1925#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1926 vps1.text = "PS1=\\w \\$ ";
1927#else
1928 if (!geteuid())
1929 vps1.text = "PS1=# ";
1930#endif
1931 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001932 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001933 do {
1934 vpp = hashvar(vp->text);
1935 vp->next = *vpp;
1936 *vpp = vp;
1937 } while (++vp < end);
1938}
1939
1940static struct var **
1941findvar(struct var **vpp, const char *name)
1942{
1943 for (; *vpp; vpp = &(*vpp)->next) {
1944 if (varequal((*vpp)->text, name)) {
1945 break;
1946 }
1947 }
1948 return vpp;
1949}
1950
1951/*
1952 * Find the value of a variable. Returns NULL if not set.
1953 */
1954static char *
1955lookupvar(const char *name)
1956{
1957 struct var *v;
1958
1959 v = *findvar(hashvar(name), name);
1960 if (v) {
1961#ifdef DYNAMIC_VAR
1962 /*
1963 * Dynamic variables are implemented roughly the same way they are
1964 * in bash. Namely, they're "special" so long as they aren't unset.
1965 * As soon as they're unset, they're no longer dynamic, and dynamic
1966 * lookup will no longer happen at that point. -- PFM.
1967 */
1968 if ((v->flags & VDYNAMIC))
1969 (*v->func)(NULL);
1970#endif
1971 if (!(v->flags & VUNSET))
1972 return strchrnul(v->text, '=') + 1;
1973 }
1974 return NULL;
1975}
1976
1977/*
1978 * Search the environment of a builtin command.
1979 */
1980static char *
1981bltinlookup(const char *name)
1982{
1983 struct strlist *sp;
1984
1985 for (sp = cmdenviron; sp; sp = sp->next) {
1986 if (varequal(sp->text, name))
1987 return strchrnul(sp->text, '=') + 1;
1988 }
1989 return lookupvar(name);
1990}
1991
1992/*
1993 * Same as setvar except that the variable and value are passed in
1994 * the first argument as name=value. Since the first argument will
1995 * be actually stored in the table, it should not be a string that
1996 * will go away.
1997 * Called with interrupts off.
1998 */
1999static void
2000setvareq(char *s, int flags)
2001{
2002 struct var *vp, **vpp;
2003
2004 vpp = hashvar(s);
2005 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2006 vp = *findvar(vpp, s);
2007 if (vp) {
2008 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2009 const char *n;
2010
2011 if (flags & VNOSAVE)
2012 free(s);
2013 n = vp->text;
2014 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2015 }
2016
2017 if (flags & VNOSET)
2018 return;
2019
2020 if (vp->func && (flags & VNOFUNC) == 0)
2021 (*vp->func)(strchrnul(s, '=') + 1);
2022
2023 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2024 free((char*)vp->text);
2025
2026 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2027 } else {
2028 if (flags & VNOSET)
2029 return;
2030 /* not found */
2031 vp = ckmalloc(sizeof(*vp));
2032 vp->next = *vpp;
2033 vp->func = NULL;
2034 *vpp = vp;
2035 }
2036 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2037 s = ckstrdup(s);
2038 vp->text = s;
2039 vp->flags = flags;
2040}
2041
2042/*
2043 * Set the value of a variable. The flags argument is ored with the
2044 * flags of the variable. If val is NULL, the variable is unset.
2045 */
2046static void
2047setvar(const char *name, const char *val, int flags)
2048{
2049 char *p, *q;
2050 size_t namelen;
2051 char *nameeq;
2052 size_t vallen;
2053
2054 q = endofname(name);
2055 p = strchrnul(q, '=');
2056 namelen = p - name;
2057 if (!namelen || p != q)
2058 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2059 vallen = 0;
2060 if (val == NULL) {
2061 flags |= VUNSET;
2062 } else {
2063 vallen = strlen(val);
2064 }
2065 INT_OFF;
2066 nameeq = ckmalloc(namelen + vallen + 2);
2067 p = memcpy(nameeq, name, namelen) + namelen;
2068 if (val) {
2069 *p++ = '=';
2070 p = memcpy(p, val, vallen) + vallen;
2071 }
2072 *p = '\0';
2073 setvareq(nameeq, flags | VNOSAVE);
2074 INT_ON;
2075}
2076
2077#if ENABLE_ASH_GETOPTS
2078/*
2079 * Safe version of setvar, returns 1 on success 0 on failure.
2080 */
2081static int
2082setvarsafe(const char *name, const char *val, int flags)
2083{
2084 int err;
2085 volatile int saveint;
2086 struct jmploc *volatile savehandler = exception_handler;
2087 struct jmploc jmploc;
2088
2089 SAVE_INT(saveint);
2090 if (setjmp(jmploc.loc))
2091 err = 1;
2092 else {
2093 exception_handler = &jmploc;
2094 setvar(name, val, flags);
2095 err = 0;
2096 }
2097 exception_handler = savehandler;
2098 RESTORE_INT(saveint);
2099 return err;
2100}
2101#endif
2102
2103/*
2104 * Unset the specified variable.
2105 */
2106static int
2107unsetvar(const char *s)
2108{
2109 struct var **vpp;
2110 struct var *vp;
2111 int retval;
2112
2113 vpp = findvar(hashvar(s), s);
2114 vp = *vpp;
2115 retval = 2;
2116 if (vp) {
2117 int flags = vp->flags;
2118
2119 retval = 1;
2120 if (flags & VREADONLY)
2121 goto out;
2122#ifdef DYNAMIC_VAR
2123 vp->flags &= ~VDYNAMIC;
2124#endif
2125 if (flags & VUNSET)
2126 goto ok;
2127 if ((flags & VSTRFIXED) == 0) {
2128 INT_OFF;
2129 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2130 free((char*)vp->text);
2131 *vpp = vp->next;
2132 free(vp);
2133 INT_ON;
2134 } else {
2135 setvar(s, 0, 0);
2136 vp->flags &= ~VEXPORT;
2137 }
2138 ok:
2139 retval = 0;
2140 }
2141 out:
2142 return retval;
2143}
2144
2145/*
2146 * Process a linked list of variable assignments.
2147 */
2148static void
2149listsetvar(struct strlist *list_set_var, int flags)
2150{
2151 struct strlist *lp = list_set_var;
2152
2153 if (!lp)
2154 return;
2155 INT_OFF;
2156 do {
2157 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002158 lp = lp->next;
2159 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002160 INT_ON;
2161}
2162
2163/*
2164 * Generate a list of variables satisfying the given conditions.
2165 */
2166static char **
2167listvars(int on, int off, char ***end)
2168{
2169 struct var **vpp;
2170 struct var *vp;
2171 char **ep;
2172 int mask;
2173
2174 STARTSTACKSTR(ep);
2175 vpp = vartab;
2176 mask = on | off;
2177 do {
2178 for (vp = *vpp; vp; vp = vp->next) {
2179 if ((vp->flags & mask) == on) {
2180 if (ep == stackstrend())
2181 ep = growstackstr();
2182 *ep++ = (char *) vp->text;
2183 }
2184 }
2185 } while (++vpp < vartab + VTABSIZE);
2186 if (ep == stackstrend())
2187 ep = growstackstr();
2188 if (end)
2189 *end = ep;
2190 *ep++ = NULL;
2191 return grabstackstr(ep);
2192}
2193
2194
2195/* ============ Path search helper
2196 *
2197 * The variable path (passed by reference) should be set to the start
2198 * of the path before the first call; padvance will update
2199 * this value as it proceeds. Successive calls to padvance will return
2200 * the possible path expansions in sequence. If an option (indicated by
2201 * a percent sign) appears in the path entry then the global variable
2202 * pathopt will be set to point to it; otherwise pathopt will be set to
2203 * NULL.
2204 */
2205static const char *pathopt; /* set by padvance */
2206
2207static char *
2208padvance(const char **path, const char *name)
2209{
2210 const char *p;
2211 char *q;
2212 const char *start;
2213 size_t len;
2214
2215 if (*path == NULL)
2216 return NULL;
2217 start = *path;
2218 for (p = start; *p && *p != ':' && *p != '%'; p++);
2219 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2220 while (stackblocksize() < len)
2221 growstackblock();
2222 q = stackblock();
2223 if (p != start) {
2224 memcpy(q, start, p - start);
2225 q += p - start;
2226 *q++ = '/';
2227 }
2228 strcpy(q, name);
2229 pathopt = NULL;
2230 if (*p == '%') {
2231 pathopt = ++p;
2232 while (*p && *p != ':') p++;
2233 }
2234 if (*p == ':')
2235 *path = p + 1;
2236 else
2237 *path = NULL;
2238 return stalloc(len);
2239}
2240
2241
2242/* ============ Prompt */
2243
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002244static smallint doprompt; /* if set, prompt the user */
2245static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002246
2247#if ENABLE_FEATURE_EDITING
2248static line_input_t *line_input_state;
2249static const char *cmdedit_prompt;
2250static void
2251putprompt(const char *s)
2252{
2253 if (ENABLE_ASH_EXPAND_PRMT) {
2254 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002255 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256 return;
2257 }
2258 cmdedit_prompt = s;
2259}
2260#else
2261static void
2262putprompt(const char *s)
2263{
2264 out2str(s);
2265}
2266#endif
2267
2268#if ENABLE_ASH_EXPAND_PRMT
2269/* expandstr() needs parsing machinery, so it is far away ahead... */
2270static const char *expandstr(const char *ps);
2271#else
2272#define expandstr(s) s
2273#endif
2274
2275static void
2276setprompt(int whichprompt)
2277{
2278 const char *prompt;
2279#if ENABLE_ASH_EXPAND_PRMT
2280 struct stackmark smark;
2281#endif
2282
2283 needprompt = 0;
2284
2285 switch (whichprompt) {
2286 case 1:
2287 prompt = ps1val();
2288 break;
2289 case 2:
2290 prompt = ps2val();
2291 break;
2292 default: /* 0 */
2293 prompt = nullstr;
2294 }
2295#if ENABLE_ASH_EXPAND_PRMT
2296 setstackmark(&smark);
2297 stalloc(stackblocksize());
2298#endif
2299 putprompt(expandstr(prompt));
2300#if ENABLE_ASH_EXPAND_PRMT
2301 popstackmark(&smark);
2302#endif
2303}
2304
2305
2306/* ============ The cd and pwd commands */
2307
2308#define CD_PHYSICAL 1
2309#define CD_PRINT 2
2310
2311static int docd(const char *, int);
2312
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002313static int
2314cdopt(void)
2315{
2316 int flags = 0;
2317 int i, j;
2318
2319 j = 'L';
2320 while ((i = nextopt("LP"))) {
2321 if (i != j) {
2322 flags ^= CD_PHYSICAL;
2323 j = i;
2324 }
2325 }
2326
2327 return flags;
2328}
2329
2330/*
2331 * Update curdir (the name of the current directory) in response to a
2332 * cd command.
2333 */
2334static const char *
2335updatepwd(const char *dir)
2336{
2337 char *new;
2338 char *p;
2339 char *cdcomppath;
2340 const char *lim;
2341
2342 cdcomppath = ststrdup(dir);
2343 STARTSTACKSTR(new);
2344 if (*dir != '/') {
2345 if (curdir == nullstr)
2346 return 0;
2347 new = stack_putstr(curdir, new);
2348 }
2349 new = makestrspace(strlen(dir) + 2, new);
2350 lim = stackblock() + 1;
2351 if (*dir != '/') {
2352 if (new[-1] != '/')
2353 USTPUTC('/', new);
2354 if (new > lim && *lim == '/')
2355 lim++;
2356 } else {
2357 USTPUTC('/', new);
2358 cdcomppath++;
2359 if (dir[1] == '/' && dir[2] != '/') {
2360 USTPUTC('/', new);
2361 cdcomppath++;
2362 lim++;
2363 }
2364 }
2365 p = strtok(cdcomppath, "/");
2366 while (p) {
2367 switch (*p) {
2368 case '.':
2369 if (p[1] == '.' && p[2] == '\0') {
2370 while (new > lim) {
2371 STUNPUTC(new);
2372 if (new[-1] == '/')
2373 break;
2374 }
2375 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002376 }
2377 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378 break;
2379 /* fall through */
2380 default:
2381 new = stack_putstr(p, new);
2382 USTPUTC('/', new);
2383 }
2384 p = strtok(0, "/");
2385 }
2386 if (new > lim)
2387 STUNPUTC(new);
2388 *new = 0;
2389 return stackblock();
2390}
2391
2392/*
2393 * Find out what the current directory is. If we already know the current
2394 * directory, this routine returns immediately.
2395 */
2396static char *
2397getpwd(void)
2398{
Denis Vlasenko01631112007-12-16 17:20:38 +00002399 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400 return dir ? dir : nullstr;
2401}
2402
2403static void
2404setpwd(const char *val, int setold)
2405{
2406 char *oldcur, *dir;
2407
2408 oldcur = dir = curdir;
2409
2410 if (setold) {
2411 setvar("OLDPWD", oldcur, VEXPORT);
2412 }
2413 INT_OFF;
2414 if (physdir != nullstr) {
2415 if (physdir != oldcur)
2416 free(physdir);
2417 physdir = nullstr;
2418 }
2419 if (oldcur == val || !val) {
2420 char *s = getpwd();
2421 physdir = s;
2422 if (!val)
2423 dir = s;
2424 } else
2425 dir = ckstrdup(val);
2426 if (oldcur != dir && oldcur != nullstr) {
2427 free(oldcur);
2428 }
2429 curdir = dir;
2430 INT_ON;
2431 setvar("PWD", dir, VEXPORT);
2432}
2433
2434static void hashcd(void);
2435
2436/*
2437 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2438 * know that the current directory has changed.
2439 */
2440static int
2441docd(const char *dest, int flags)
2442{
2443 const char *dir = 0;
2444 int err;
2445
2446 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2447
2448 INT_OFF;
2449 if (!(flags & CD_PHYSICAL)) {
2450 dir = updatepwd(dest);
2451 if (dir)
2452 dest = dir;
2453 }
2454 err = chdir(dest);
2455 if (err)
2456 goto out;
2457 setpwd(dir, 1);
2458 hashcd();
2459 out:
2460 INT_ON;
2461 return err;
2462}
2463
2464static int
2465cdcmd(int argc, char **argv)
2466{
2467 const char *dest;
2468 const char *path;
2469 const char *p;
2470 char c;
2471 struct stat statb;
2472 int flags;
2473
2474 flags = cdopt();
2475 dest = *argptr;
2476 if (!dest)
2477 dest = bltinlookup(homestr);
2478 else if (LONE_DASH(dest)) {
2479 dest = bltinlookup("OLDPWD");
2480 flags |= CD_PRINT;
2481 }
2482 if (!dest)
2483 dest = nullstr;
2484 if (*dest == '/')
2485 goto step7;
2486 if (*dest == '.') {
2487 c = dest[1];
2488 dotdot:
2489 switch (c) {
2490 case '\0':
2491 case '/':
2492 goto step6;
2493 case '.':
2494 c = dest[2];
2495 if (c != '.')
2496 goto dotdot;
2497 }
2498 }
2499 if (!*dest)
2500 dest = ".";
2501 path = bltinlookup("CDPATH");
2502 if (!path) {
2503 step6:
2504 step7:
2505 p = dest;
2506 goto docd;
2507 }
2508 do {
2509 c = *path;
2510 p = padvance(&path, dest);
2511 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2512 if (c && c != ':')
2513 flags |= CD_PRINT;
2514 docd:
2515 if (!docd(p, flags))
2516 goto out;
2517 break;
2518 }
2519 } while (path);
2520 ash_msg_and_raise_error("can't cd to %s", dest);
2521 /* NOTREACHED */
2522 out:
2523 if (flags & CD_PRINT)
2524 out1fmt(snlfmt, curdir);
2525 return 0;
2526}
2527
2528static int
2529pwdcmd(int argc, char **argv)
2530{
2531 int flags;
2532 const char *dir = curdir;
2533
2534 flags = cdopt();
2535 if (flags) {
2536 if (physdir == nullstr)
2537 setpwd(dir, 0);
2538 dir = physdir;
2539 }
2540 out1fmt(snlfmt, dir);
2541 return 0;
2542}
2543
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002544
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002545/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002546
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002547#define IBUFSIZ COMMON_BUFSIZE
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002548#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
Eric Andersenc470f442003-07-28 09:56:35 +00002549
Eric Andersenc470f442003-07-28 09:56:35 +00002550/* Syntax classes */
2551#define CWORD 0 /* character is nothing special */
2552#define CNL 1 /* newline character */
2553#define CBACK 2 /* a backslash character */
2554#define CSQUOTE 3 /* single quote */
2555#define CDQUOTE 4 /* double quote */
2556#define CENDQUOTE 5 /* a terminating quote */
2557#define CBQUOTE 6 /* backwards single quote */
2558#define CVAR 7 /* a dollar sign */
2559#define CENDVAR 8 /* a '}' character */
2560#define CLP 9 /* a left paren in arithmetic */
2561#define CRP 10 /* a right paren in arithmetic */
2562#define CENDFILE 11 /* end of file */
2563#define CCTL 12 /* like CWORD, except it must be escaped */
2564#define CSPCL 13 /* these terminate a word */
2565#define CIGN 14 /* character should be ignored */
2566
Denis Vlasenko131ae172007-02-18 13:00:19 +00002567#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002568#define SYNBASE 130
2569#define PEOF -130
2570#define PEOA -129
2571#define PEOA_OR_PEOF PEOA
2572#else
2573#define SYNBASE 129
2574#define PEOF -129
2575#define PEOA_OR_PEOF PEOF
2576#endif
2577
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002578/* number syntax index */
2579#define BASESYNTAX 0 /* not in quotes */
2580#define DQSYNTAX 1 /* in double quotes */
2581#define SQSYNTAX 2 /* in single quotes */
2582#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002583#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002584
Denis Vlasenko131ae172007-02-18 13:00:19 +00002585#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002586#define USE_SIT_FUNCTION
2587#endif
2588
Denis Vlasenko131ae172007-02-18 13:00:19 +00002589#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002590static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002591#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002592 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002593#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002594 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2595 { CNL, CNL, CNL, CNL }, /* 2, \n */
2596 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2597 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2598 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2599 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2600 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2601 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2602 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2603 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2604 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002605#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002606 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2607 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2608 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002609#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002610};
Eric Andersenc470f442003-07-28 09:56:35 +00002611#else
2612static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002613#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002614 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002615#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002616 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2617 { CNL, CNL, CNL }, /* 2, \n */
2618 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2619 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2620 { CVAR, CVAR, CWORD }, /* 5, $ */
2621 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2622 { CSPCL, CWORD, CWORD }, /* 7, ( */
2623 { CSPCL, CWORD, CWORD }, /* 8, ) */
2624 { CBACK, CBACK, CCTL }, /* 9, \ */
2625 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2626 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002627#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002628 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2629 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2630 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002631#endif
2632};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002633#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002634
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002635#ifdef USE_SIT_FUNCTION
2636
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002637static int
2638SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002639{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002640 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002641#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002642 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002643 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2644 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2645 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2646 11, 3 /* "}~" */
2647 };
2648#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002649 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002650 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2651 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2652 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2653 10, 2 /* "}~" */
2654 };
2655#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656 const char *s;
2657 int indx;
2658
Eric Andersenc470f442003-07-28 09:56:35 +00002659 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002660 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002661#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002662 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002663 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002664 else
2665#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002666#define U_C(c) ((unsigned char)(c))
2667
2668 if ((unsigned char)c >= (unsigned char)(CTLESC)
2669 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2670 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002671 return CCTL;
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002672 } else {
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002673 s = strchr(spec_symbls, c);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002674 if (s == NULL || *s == '\0')
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002675 return CWORD;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002676 indx = syntax_index_table[(s - spec_symbls)];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002677 }
2678 return S_I_T[indx][syntax];
2679}
2680
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002681#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002682
Denis Vlasenko131ae172007-02-18 13:00:19 +00002683#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002684#define CSPCL_CIGN_CIGN_CIGN 0
2685#define CSPCL_CWORD_CWORD_CWORD 1
2686#define CNL_CNL_CNL_CNL 2
2687#define CWORD_CCTL_CCTL_CWORD 3
2688#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2689#define CVAR_CVAR_CWORD_CVAR 5
2690#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2691#define CSPCL_CWORD_CWORD_CLP 7
2692#define CSPCL_CWORD_CWORD_CRP 8
2693#define CBACK_CBACK_CCTL_CBACK 9
2694#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2695#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2696#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2697#define CWORD_CWORD_CWORD_CWORD 13
2698#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002699#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002700#define CSPCL_CWORD_CWORD_CWORD 0
2701#define CNL_CNL_CNL_CNL 1
2702#define CWORD_CCTL_CCTL_CWORD 2
2703#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2704#define CVAR_CVAR_CWORD_CVAR 4
2705#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2706#define CSPCL_CWORD_CWORD_CLP 6
2707#define CSPCL_CWORD_CWORD_CRP 7
2708#define CBACK_CBACK_CCTL_CBACK 8
2709#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2710#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2711#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2712#define CWORD_CWORD_CWORD_CWORD 12
2713#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002714#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002715
2716static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002717 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002718 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002719#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002720 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2721#endif
2722 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2723 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2724 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2725 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2726 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2727 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2728 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2729 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2730 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002731 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2732 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2733 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2734 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2735 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2736 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2737 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2738 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2739 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2740 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2741 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2742 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2743 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2744 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2745 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2746 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2747 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2748 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2749 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2750 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2751 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2752 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2753 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2754 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2755 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2756 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2757 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2758 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2759 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2760 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2761 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2762 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2763 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2764 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2765 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2767 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2768 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2769 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2770 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2771 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2773 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2774 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2860 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2861 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2883 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002884 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002885 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2886 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2887 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002889 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002890 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2891 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2892 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2893 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2895 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2896 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2897 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2898 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2909 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2910 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2911 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2912 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2913 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2914 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2942 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2943 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2944 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2947 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2975 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2976 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2977 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00002978};
2979
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002980#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
2981
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00002982#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00002983
Eric Andersen2870d962001-07-02 17:27:21 +00002984
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002985/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00002986
Denis Vlasenko131ae172007-02-18 13:00:19 +00002987#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002988
2989#define ALIASINUSE 1
2990#define ALIASDEAD 2
2991
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00002992struct alias {
2993 struct alias *next;
2994 char *name;
2995 char *val;
2996 int flag;
2997};
2998
Denis Vlasenko01631112007-12-16 17:20:38 +00002999
3000static struct alias **atab; // [ATABSIZE];
3001#define INIT_G_alias() do { \
3002 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3003} while (0)
3004
Eric Andersen2870d962001-07-02 17:27:21 +00003005
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003006static struct alias **
3007__lookupalias(const char *name) {
3008 unsigned int hashval;
3009 struct alias **app;
3010 const char *p;
3011 unsigned int ch;
3012
3013 p = name;
3014
3015 ch = (unsigned char)*p;
3016 hashval = ch << 4;
3017 while (ch) {
3018 hashval += ch;
3019 ch = (unsigned char)*++p;
3020 }
3021 app = &atab[hashval % ATABSIZE];
3022
3023 for (; *app; app = &(*app)->next) {
3024 if (strcmp(name, (*app)->name) == 0) {
3025 break;
3026 }
3027 }
3028
3029 return app;
3030}
3031
3032static struct alias *
3033lookupalias(const char *name, int check)
3034{
3035 struct alias *ap = *__lookupalias(name);
3036
3037 if (check && ap && (ap->flag & ALIASINUSE))
3038 return NULL;
3039 return ap;
3040}
3041
3042static struct alias *
3043freealias(struct alias *ap)
3044{
3045 struct alias *next;
3046
3047 if (ap->flag & ALIASINUSE) {
3048 ap->flag |= ALIASDEAD;
3049 return ap;
3050 }
3051
3052 next = ap->next;
3053 free(ap->name);
3054 free(ap->val);
3055 free(ap);
3056 return next;
3057}
Eric Andersencb57d552001-06-28 07:25:16 +00003058
Eric Andersenc470f442003-07-28 09:56:35 +00003059static void
3060setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003061{
3062 struct alias *ap, **app;
3063
3064 app = __lookupalias(name);
3065 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003066 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003067 if (ap) {
3068 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003069 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003070 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003071 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003072 ap->flag &= ~ALIASDEAD;
3073 } else {
3074 /* not found */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00003075 ap = ckmalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003076 ap->name = ckstrdup(name);
3077 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003078 ap->flag = 0;
3079 ap->next = 0;
3080 *app = ap;
3081 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003082 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003083}
3084
Eric Andersenc470f442003-07-28 09:56:35 +00003085static int
3086unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003087{
Eric Andersencb57d552001-06-28 07:25:16 +00003088 struct alias **app;
3089
3090 app = __lookupalias(name);
3091
3092 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003093 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003094 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003095 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003096 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003097 }
3098
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003099 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003100}
3101
Eric Andersenc470f442003-07-28 09:56:35 +00003102static void
3103rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003104{
Eric Andersencb57d552001-06-28 07:25:16 +00003105 struct alias *ap, **app;
3106 int i;
3107
Denis Vlasenkob012b102007-02-19 22:43:01 +00003108 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003109 for (i = 0; i < ATABSIZE; i++) {
3110 app = &atab[i];
3111 for (ap = *app; ap; ap = *app) {
3112 *app = freealias(*app);
3113 if (ap == *app) {
3114 app = &ap->next;
3115 }
3116 }
3117 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003119}
3120
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003121static void
3122printalias(const struct alias *ap)
3123{
3124 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3125}
3126
Eric Andersencb57d552001-06-28 07:25:16 +00003127/*
3128 * TODO - sort output
3129 */
Eric Andersenc470f442003-07-28 09:56:35 +00003130static int
3131aliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003132{
3133 char *n, *v;
3134 int ret = 0;
3135 struct alias *ap;
3136
3137 if (argc == 1) {
3138 int i;
3139
3140 for (i = 0; i < ATABSIZE; i++)
3141 for (ap = atab[i]; ap; ap = ap->next) {
3142 printalias(ap);
3143 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003144 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003145 }
3146 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003147 v = strchr(n+1, '=');
3148 if (v == NULL) { /* n+1: funny ksh stuff */
3149 ap = *__lookupalias(n);
3150 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003151 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003152 ret = 1;
3153 } else
3154 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003155 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003156 *v++ = '\0';
3157 setalias(n, v);
3158 }
3159 }
3160
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003161 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003162}
3163
Eric Andersenc470f442003-07-28 09:56:35 +00003164static int
3165unaliascmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003166{
3167 int i;
3168
3169 while ((i = nextopt("a")) != '\0') {
3170 if (i == 'a') {
3171 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003172 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003173 }
3174 }
3175 for (i = 0; *argptr; argptr++) {
3176 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003177 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003178 i = 1;
3179 }
3180 }
3181
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003182 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003183}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003184
Denis Vlasenko131ae172007-02-18 13:00:19 +00003185#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003186
Eric Andersenc470f442003-07-28 09:56:35 +00003187
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003188/* ============ jobs.c */
3189
3190/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3191#define FORK_FG 0
3192#define FORK_BG 1
3193#define FORK_NOJOB 2
3194
3195/* mode flags for showjob(s) */
3196#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3197#define SHOW_PID 0x04 /* include process pid */
3198#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3199
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003200/*
3201 * A job structure contains information about a job. A job is either a
3202 * single process or a set of processes contained in a pipeline. In the
3203 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3204 * array of pids.
3205 */
3206
3207struct procstat {
3208 pid_t pid; /* process id */
3209 int status; /* last process status from wait() */
3210 char *cmd; /* text of command being run */
3211};
3212
3213struct job {
3214 struct procstat ps0; /* status of process */
3215 struct procstat *ps; /* status or processes when more than one */
3216#if JOBS
3217 int stopstatus; /* status of a stopped job */
3218#endif
3219 uint32_t
3220 nprocs: 16, /* number of processes */
3221 state: 8,
3222#define JOBRUNNING 0 /* at least one proc running */
3223#define JOBSTOPPED 1 /* all procs are stopped */
3224#define JOBDONE 2 /* all procs are completed */
3225#if JOBS
3226 sigint: 1, /* job was killed by SIGINT */
3227 jobctl: 1, /* job running under job control */
3228#endif
3229 waited: 1, /* true if this entry has been waited for */
3230 used: 1, /* true if this entry is in used */
3231 changed: 1; /* true if status has changed */
3232 struct job *prev_job; /* previous job */
3233};
3234
3235static pid_t backgndpid; /* pid of last background process */
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003236static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003237
3238static struct job *makejob(union node *, int);
3239static int forkshell(struct job *, union node *, int);
3240static int waitforjob(struct job *);
3241
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003242#if !JOBS
3243enum { jobctl = 0 };
3244#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003245#else
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003246static smallint jobctl; /* true if doing job control */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003247static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003248#endif
3249
3250/*
3251 * Set the signal handler for the specified signal. The routine figures
3252 * out what it should be set to.
3253 */
3254static void
3255setsignal(int signo)
3256{
3257 int action;
3258 char *t, tsig;
3259 struct sigaction act;
3260
3261 t = trap[signo];
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003262 action = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003263 if (t == NULL)
3264 action = S_DFL;
3265 else if (*t != '\0')
3266 action = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003267 if (rootshell && action == S_DFL) {
3268 switch (signo) {
3269 case SIGINT:
3270 if (iflag || minusc || sflag == 0)
3271 action = S_CATCH;
3272 break;
3273 case SIGQUIT:
3274#if DEBUG
3275 if (debug)
3276 break;
3277#endif
3278 /* FALLTHROUGH */
3279 case SIGTERM:
3280 if (iflag)
3281 action = S_IGN;
3282 break;
3283#if JOBS
3284 case SIGTSTP:
3285 case SIGTTOU:
3286 if (mflag)
3287 action = S_IGN;
3288 break;
3289#endif
3290 }
3291 }
3292
3293 t = &sigmode[signo - 1];
3294 tsig = *t;
3295 if (tsig == 0) {
3296 /*
3297 * current setting unknown
3298 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003299 if (sigaction(signo, NULL, &act) == -1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003300 /*
3301 * Pretend it worked; maybe we should give a warning
3302 * here, but other shells don't. We don't alter
3303 * sigmode, so that we retry every time.
3304 */
3305 return;
3306 }
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003307 tsig = S_RESET; /* force to be set */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003308 if (act.sa_handler == SIG_IGN) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003309 tsig = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003310 if (mflag
3311 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3312 ) {
3313 tsig = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003314 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003315 }
3316 }
3317 if (tsig == S_HARD_IGN || tsig == action)
3318 return;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003319 act.sa_handler = SIG_DFL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320 switch (action) {
3321 case S_CATCH:
3322 act.sa_handler = onsig;
3323 break;
3324 case S_IGN:
3325 act.sa_handler = SIG_IGN;
3326 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327 }
3328 *t = action;
3329 act.sa_flags = 0;
3330 sigfillset(&act.sa_mask);
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003331 sigaction(signo, &act, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332}
3333
3334/* mode flags for set_curjob */
3335#define CUR_DELETE 2
3336#define CUR_RUNNING 1
3337#define CUR_STOPPED 0
3338
3339/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003340#define DOWAIT_NONBLOCK WNOHANG
3341#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342
3343#if JOBS
3344/* pgrp of shell on invocation */
3345static int initialpgrp;
3346static int ttyfd = -1;
3347#endif
3348/* array of jobs */
3349static struct job *jobtab;
3350/* size of array */
3351static unsigned njobs;
3352/* current job */
3353static struct job *curjob;
3354/* number of presumed living untracked jobs */
3355static int jobless;
3356
3357static void
3358set_curjob(struct job *jp, unsigned mode)
3359{
3360 struct job *jp1;
3361 struct job **jpp, **curp;
3362
3363 /* first remove from list */
3364 jpp = curp = &curjob;
3365 do {
3366 jp1 = *jpp;
3367 if (jp1 == jp)
3368 break;
3369 jpp = &jp1->prev_job;
3370 } while (1);
3371 *jpp = jp1->prev_job;
3372
3373 /* Then re-insert in correct position */
3374 jpp = curp;
3375 switch (mode) {
3376 default:
3377#if DEBUG
3378 abort();
3379#endif
3380 case CUR_DELETE:
3381 /* job being deleted */
3382 break;
3383 case CUR_RUNNING:
3384 /* newly created job or backgrounded job,
3385 put after all stopped jobs. */
3386 do {
3387 jp1 = *jpp;
3388#if JOBS
3389 if (!jp1 || jp1->state != JOBSTOPPED)
3390#endif
3391 break;
3392 jpp = &jp1->prev_job;
3393 } while (1);
3394 /* FALLTHROUGH */
3395#if JOBS
3396 case CUR_STOPPED:
3397#endif
3398 /* newly stopped job - becomes curjob */
3399 jp->prev_job = *jpp;
3400 *jpp = jp;
3401 break;
3402 }
3403}
3404
3405#if JOBS || DEBUG
3406static int
3407jobno(const struct job *jp)
3408{
3409 return jp - jobtab + 1;
3410}
3411#endif
3412
3413/*
3414 * Convert a job name to a job structure.
3415 */
3416static struct job *
3417getjob(const char *name, int getctl)
3418{
3419 struct job *jp;
3420 struct job *found;
3421 const char *err_msg = "No such job: %s";
3422 unsigned num;
3423 int c;
3424 const char *p;
3425 char *(*match)(const char *, const char *);
3426
3427 jp = curjob;
3428 p = name;
3429 if (!p)
3430 goto currentjob;
3431
3432 if (*p != '%')
3433 goto err;
3434
3435 c = *++p;
3436 if (!c)
3437 goto currentjob;
3438
3439 if (!p[1]) {
3440 if (c == '+' || c == '%') {
3441 currentjob:
3442 err_msg = "No current job";
3443 goto check;
3444 }
3445 if (c == '-') {
3446 if (jp)
3447 jp = jp->prev_job;
3448 err_msg = "No previous job";
3449 check:
3450 if (!jp)
3451 goto err;
3452 goto gotit;
3453 }
3454 }
3455
3456 if (is_number(p)) {
3457 num = atoi(p);
3458 if (num < njobs) {
3459 jp = jobtab + num - 1;
3460 if (jp->used)
3461 goto gotit;
3462 goto err;
3463 }
3464 }
3465
3466 match = prefix;
3467 if (*p == '?') {
3468 match = strstr;
3469 p++;
3470 }
3471
3472 found = 0;
3473 while (1) {
3474 if (!jp)
3475 goto err;
3476 if (match(jp->ps[0].cmd, p)) {
3477 if (found)
3478 goto err;
3479 found = jp;
3480 err_msg = "%s: ambiguous";
3481 }
3482 jp = jp->prev_job;
3483 }
3484
3485 gotit:
3486#if JOBS
3487 err_msg = "job %s not created under job control";
3488 if (getctl && jp->jobctl == 0)
3489 goto err;
3490#endif
3491 return jp;
3492 err:
3493 ash_msg_and_raise_error(err_msg, name);
3494}
3495
3496/*
3497 * Mark a job structure as unused.
3498 */
3499static void
3500freejob(struct job *jp)
3501{
3502 struct procstat *ps;
3503 int i;
3504
3505 INT_OFF;
3506 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3507 if (ps->cmd != nullstr)
3508 free(ps->cmd);
3509 }
3510 if (jp->ps != &jp->ps0)
3511 free(jp->ps);
3512 jp->used = 0;
3513 set_curjob(jp, CUR_DELETE);
3514 INT_ON;
3515}
3516
3517#if JOBS
3518static void
3519xtcsetpgrp(int fd, pid_t pgrp)
3520{
3521 if (tcsetpgrp(fd, pgrp))
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00003522 ash_msg_and_raise_error("cannot set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003523}
3524
3525/*
3526 * Turn job control on and off.
3527 *
3528 * Note: This code assumes that the third arg to ioctl is a character
3529 * pointer, which is true on Berkeley systems but not System V. Since
3530 * System V doesn't have job control yet, this isn't a problem now.
3531 *
3532 * Called with interrupts off.
3533 */
3534static void
3535setjobctl(int on)
3536{
3537 int fd;
3538 int pgrp;
3539
3540 if (on == jobctl || rootshell == 0)
3541 return;
3542 if (on) {
3543 int ofd;
3544 ofd = fd = open(_PATH_TTY, O_RDWR);
3545 if (fd < 0) {
3546 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3547 * That sometimes helps to acquire controlling tty.
3548 * Obviously, a workaround for bugs when someone
3549 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003550 fd = 2;
3551 while (!isatty(fd))
3552 if (--fd < 0)
3553 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003554 }
3555 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003556 if (ofd >= 0)
3557 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003558 if (fd < 0)
3559 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003560 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003561 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003562 do { /* while we are in the background */
3563 pgrp = tcgetpgrp(fd);
3564 if (pgrp < 0) {
3565 out:
3566 ash_msg("can't access tty; job control turned off");
3567 mflag = on = 0;
3568 goto close;
3569 }
3570 if (pgrp == getpgrp())
3571 break;
3572 killpg(0, SIGTTIN);
3573 } while (1);
3574 initialpgrp = pgrp;
3575
3576 setsignal(SIGTSTP);
3577 setsignal(SIGTTOU);
3578 setsignal(SIGTTIN);
3579 pgrp = rootpid;
3580 setpgid(0, pgrp);
3581 xtcsetpgrp(fd, pgrp);
3582 } else {
3583 /* turning job control off */
3584 fd = ttyfd;
3585 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003586 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003587 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003588 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003589 setpgid(0, pgrp);
3590 setsignal(SIGTSTP);
3591 setsignal(SIGTTOU);
3592 setsignal(SIGTTIN);
3593 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003594 if (fd >= 0)
3595 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596 fd = -1;
3597 }
3598 ttyfd = fd;
3599 jobctl = on;
3600}
3601
3602static int
3603killcmd(int argc, char **argv)
3604{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003605 if (argv[1] && strcmp(argv[1], "-l") != 0) {
3606 int i = 1;
3607 do {
3608 if (argv[i][0] == '%') {
3609 struct job *jp = getjob(argv[i], 0);
3610 unsigned pid = jp->ps[0].pid;
3611 /* Enough space for ' -NNN<nul>' */
3612 argv[i] = alloca(sizeof(int)*3 + 3);
3613 /* kill_main has matching code to expect
3614 * leading space. Needed to not confuse
3615 * negative pids with "kill -SIGNAL_NO" syntax */
3616 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003617 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003618 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003619 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003620 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003621}
3622
3623static void
3624showpipe(struct job *jp, FILE *out)
3625{
3626 struct procstat *sp;
3627 struct procstat *spend;
3628
3629 spend = jp->ps + jp->nprocs;
3630 for (sp = jp->ps + 1; sp < spend; sp++)
3631 fprintf(out, " | %s", sp->cmd);
3632 outcslow('\n', out);
3633 flush_stdout_stderr();
3634}
3635
3636
3637static int
3638restartjob(struct job *jp, int mode)
3639{
3640 struct procstat *ps;
3641 int i;
3642 int status;
3643 pid_t pgid;
3644
3645 INT_OFF;
3646 if (jp->state == JOBDONE)
3647 goto out;
3648 jp->state = JOBRUNNING;
3649 pgid = jp->ps->pid;
3650 if (mode == FORK_FG)
3651 xtcsetpgrp(ttyfd, pgid);
3652 killpg(pgid, SIGCONT);
3653 ps = jp->ps;
3654 i = jp->nprocs;
3655 do {
3656 if (WIFSTOPPED(ps->status)) {
3657 ps->status = -1;
3658 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003659 ps++;
3660 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003661 out:
3662 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3663 INT_ON;
3664 return status;
3665}
3666
3667static int
3668fg_bgcmd(int argc, char **argv)
3669{
3670 struct job *jp;
3671 FILE *out;
3672 int mode;
3673 int retval;
3674
3675 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3676 nextopt(nullstr);
3677 argv = argptr;
3678 out = stdout;
3679 do {
3680 jp = getjob(*argv, 1);
3681 if (mode == FORK_BG) {
3682 set_curjob(jp, CUR_RUNNING);
3683 fprintf(out, "[%d] ", jobno(jp));
3684 }
3685 outstr(jp->ps->cmd, out);
3686 showpipe(jp, out);
3687 retval = restartjob(jp, mode);
3688 } while (*argv && *++argv);
3689 return retval;
3690}
3691#endif
3692
3693static int
3694sprint_status(char *s, int status, int sigonly)
3695{
3696 int col;
3697 int st;
3698
3699 col = 0;
3700 if (!WIFEXITED(status)) {
3701#if JOBS
3702 if (WIFSTOPPED(status))
3703 st = WSTOPSIG(status);
3704 else
3705#endif
3706 st = WTERMSIG(status);
3707 if (sigonly) {
3708 if (st == SIGINT || st == SIGPIPE)
3709 goto out;
3710#if JOBS
3711 if (WIFSTOPPED(status))
3712 goto out;
3713#endif
3714 }
3715 st &= 0x7f;
3716 col = fmtstr(s, 32, strsignal(st));
3717 if (WCOREDUMP(status)) {
3718 col += fmtstr(s + col, 16, " (core dumped)");
3719 }
3720 } else if (!sigonly) {
3721 st = WEXITSTATUS(status);
3722 if (st)
3723 col = fmtstr(s, 16, "Done(%d)", st);
3724 else
3725 col = fmtstr(s, 16, "Done");
3726 }
3727 out:
3728 return col;
3729}
3730
3731/*
3732 * Do a wait system call. If job control is compiled in, we accept
3733 * stopped processes. If block is zero, we return a value of zero
3734 * rather than blocking.
3735 *
3736 * System V doesn't have a non-blocking wait system call. It does
3737 * have a SIGCLD signal that is sent to a process when one of it's
3738 * children dies. The obvious way to use SIGCLD would be to install
3739 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3740 * was received, and have waitproc bump another counter when it got
3741 * the status of a process. Waitproc would then know that a wait
3742 * system call would not block if the two counters were different.
3743 * This approach doesn't work because if a process has children that
3744 * have not been waited for, System V will send it a SIGCLD when it
3745 * installs a signal handler for SIGCLD. What this means is that when
3746 * a child exits, the shell will be sent SIGCLD signals continuously
3747 * until is runs out of stack space, unless it does a wait call before
3748 * restoring the signal handler. The code below takes advantage of
3749 * this (mis)feature by installing a signal handler for SIGCLD and
3750 * then checking to see whether it was called. If there are any
3751 * children to be waited for, it will be.
3752 *
3753 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3754 * waits at all. In this case, the user will not be informed when
3755 * a background process until the next time she runs a real program
3756 * (as opposed to running a builtin command or just typing return),
3757 * and the jobs command may give out of date information.
3758 */
3759static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003760waitproc(int wait_flags, int *status)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003761{
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762#if JOBS
3763 if (jobctl)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003764 wait_flags |= WUNTRACED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765#endif
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003766 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3767 return waitpid(-1, status, wait_flags);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003768}
3769
3770/*
3771 * Wait for a process to terminate.
3772 */
3773static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003774dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775{
3776 int pid;
3777 int status;
3778 struct job *jp;
3779 struct job *thisjob;
3780 int state;
3781
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003782 TRACE(("dowait(%d) called\n", wait_flags));
3783 pid = waitproc(wait_flags, &status);
3784 TRACE(("wait returns pid=%d, status=%d\n", pid, status));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003785 if (pid <= 0) {
3786 /* If we were doing blocking wait and (probably) got EINTR,
3787 * check for pending sigs received while waiting.
3788 * (NB: can be moved into callers if needed) */
3789 if (wait_flags == DOWAIT_BLOCK && pendingsig)
3790 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791 return pid;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003792 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003793 INT_OFF;
3794 thisjob = NULL;
3795 for (jp = curjob; jp; jp = jp->prev_job) {
3796 struct procstat *sp;
3797 struct procstat *spend;
3798 if (jp->state == JOBDONE)
3799 continue;
3800 state = JOBDONE;
3801 spend = jp->ps + jp->nprocs;
3802 sp = jp->ps;
3803 do {
3804 if (sp->pid == pid) {
3805 TRACE(("Job %d: changing status of proc %d "
3806 "from 0x%x to 0x%x\n",
3807 jobno(jp), pid, sp->status, status));
3808 sp->status = status;
3809 thisjob = jp;
3810 }
3811 if (sp->status == -1)
3812 state = JOBRUNNING;
3813#if JOBS
3814 if (state == JOBRUNNING)
3815 continue;
3816 if (WIFSTOPPED(sp->status)) {
3817 jp->stopstatus = sp->status;
3818 state = JOBSTOPPED;
3819 }
3820#endif
3821 } while (++sp < spend);
3822 if (thisjob)
3823 goto gotjob;
3824 }
3825#if JOBS
3826 if (!WIFSTOPPED(status))
3827#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003828 jobless--;
3829 goto out;
3830
3831 gotjob:
3832 if (state != JOBRUNNING) {
3833 thisjob->changed = 1;
3834
3835 if (thisjob->state != state) {
3836 TRACE(("Job %d: changing state from %d to %d\n",
3837 jobno(thisjob), thisjob->state, state));
3838 thisjob->state = state;
3839#if JOBS
3840 if (state == JOBSTOPPED) {
3841 set_curjob(thisjob, CUR_STOPPED);
3842 }
3843#endif
3844 }
3845 }
3846
3847 out:
3848 INT_ON;
3849
3850 if (thisjob && thisjob == job) {
3851 char s[48 + 1];
3852 int len;
3853
3854 len = sprint_status(s, status, 1);
3855 if (len) {
3856 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003857 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003858 out2str(s);
3859 }
3860 }
3861 return pid;
3862}
3863
3864#if JOBS
3865static void
3866showjob(FILE *out, struct job *jp, int mode)
3867{
3868 struct procstat *ps;
3869 struct procstat *psend;
3870 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003871 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003872 char s[80];
3873
3874 ps = jp->ps;
3875
3876 if (mode & SHOW_PGID) {
3877 /* just output process (group) id of pipeline */
3878 fprintf(out, "%d\n", ps->pid);
3879 return;
3880 }
3881
3882 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003883 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884
3885 if (jp == curjob)
3886 s[col - 2] = '+';
3887 else if (curjob && jp == curjob->prev_job)
3888 s[col - 2] = '-';
3889
3890 if (mode & SHOW_PID)
3891 col += fmtstr(s + col, 16, "%d ", ps->pid);
3892
3893 psend = ps + jp->nprocs;
3894
3895 if (jp->state == JOBRUNNING) {
3896 strcpy(s + col, "Running");
3897 col += sizeof("Running") - 1;
3898 } else {
3899 int status = psend[-1].status;
3900 if (jp->state == JOBSTOPPED)
3901 status = jp->stopstatus;
3902 col += sprint_status(s + col, status, 0);
3903 }
3904
3905 goto start;
3906
3907 do {
3908 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003909 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003910 start:
3911 fprintf(out, "%s%*c%s",
3912 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3913 );
3914 if (!(mode & SHOW_PID)) {
3915 showpipe(jp, out);
3916 break;
3917 }
3918 if (++ps == psend) {
3919 outcslow('\n', out);
3920 break;
3921 }
3922 } while (1);
3923
3924 jp->changed = 0;
3925
3926 if (jp->state == JOBDONE) {
3927 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3928 freejob(jp);
3929 }
3930}
3931
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932/*
3933 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3934 * statuses have changed since the last call to showjobs.
3935 */
3936static void
3937showjobs(FILE *out, int mode)
3938{
3939 struct job *jp;
3940
3941 TRACE(("showjobs(%x) called\n", mode));
3942
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003943 /* If not even one job changed, there is nothing to do */
3944 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003945 continue;
3946
3947 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003948 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003950 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003951 }
3952}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003953
3954static int
3955jobscmd(int argc, char **argv)
3956{
3957 int mode, m;
3958
3959 mode = 0;
3960 while ((m = nextopt("lp"))) {
3961 if (m == 'l')
3962 mode = SHOW_PID;
3963 else
3964 mode = SHOW_PGID;
3965 }
3966
3967 argv = argptr;
3968 if (*argv) {
3969 do
3970 showjob(stdout, getjob(*argv,0), mode);
3971 while (*++argv);
3972 } else
3973 showjobs(stdout, mode);
3974
3975 return 0;
3976}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003977#endif /* JOBS */
3978
3979static int
3980getstatus(struct job *job)
3981{
3982 int status;
3983 int retval;
3984
3985 status = job->ps[job->nprocs - 1].status;
3986 retval = WEXITSTATUS(status);
3987 if (!WIFEXITED(status)) {
3988#if JOBS
3989 retval = WSTOPSIG(status);
3990 if (!WIFSTOPPED(status))
3991#endif
3992 {
3993 /* XXX: limits number of signals */
3994 retval = WTERMSIG(status);
3995#if JOBS
3996 if (retval == SIGINT)
3997 job->sigint = 1;
3998#endif
3999 }
4000 retval += 128;
4001 }
4002 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4003 jobno(job), job->nprocs, status, retval));
4004 return retval;
4005}
4006
4007static int
4008waitcmd(int argc, char **argv)
4009{
4010 struct job *job;
4011 int retval;
4012 struct job *jp;
4013
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004014// exsig++;
4015// xbarrier();
4016 if (pendingsig)
4017 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004018
4019 nextopt(nullstr);
4020 retval = 0;
4021
4022 argv = argptr;
4023 if (!*argv) {
4024 /* wait for all jobs */
4025 for (;;) {
4026 jp = curjob;
4027 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004028 if (!jp) /* no running procs */
4029 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030 if (jp->state == JOBRUNNING)
4031 break;
4032 jp->waited = 1;
4033 jp = jp->prev_job;
4034 }
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004035 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004036 }
4037 }
4038
4039 retval = 127;
4040 do {
4041 if (**argv != '%') {
4042 pid_t pid = number(*argv);
4043 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004044 while (1) {
4045 if (!job)
4046 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 if (job->ps[job->nprocs - 1].pid == pid)
4048 break;
4049 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004050 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051 } else
4052 job = getjob(*argv, 0);
4053 /* loop until process terminated or stopped */
4054 while (job->state == JOBRUNNING)
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004055 dowait(DOWAIT_BLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004056 job->waited = 1;
4057 retval = getstatus(job);
4058 repeat:
4059 ;
4060 } while (*++argv);
4061
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004062 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 return retval;
4064}
4065
4066static struct job *
4067growjobtab(void)
4068{
4069 size_t len;
4070 ptrdiff_t offset;
4071 struct job *jp, *jq;
4072
4073 len = njobs * sizeof(*jp);
4074 jq = jobtab;
4075 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4076
4077 offset = (char *)jp - (char *)jq;
4078 if (offset) {
4079 /* Relocate pointers */
4080 size_t l = len;
4081
4082 jq = (struct job *)((char *)jq + l);
4083 while (l) {
4084 l -= sizeof(*jp);
4085 jq--;
4086#define joff(p) ((struct job *)((char *)(p) + l))
4087#define jmove(p) (p) = (void *)((char *)(p) + offset)
4088 if (joff(jp)->ps == &jq->ps0)
4089 jmove(joff(jp)->ps);
4090 if (joff(jp)->prev_job)
4091 jmove(joff(jp)->prev_job);
4092 }
4093 if (curjob)
4094 jmove(curjob);
4095#undef joff
4096#undef jmove
4097 }
4098
4099 njobs += 4;
4100 jobtab = jp;
4101 jp = (struct job *)((char *)jp + len);
4102 jq = jp + 3;
4103 do {
4104 jq->used = 0;
4105 } while (--jq >= jp);
4106 return jp;
4107}
4108
4109/*
4110 * Return a new job structure.
4111 * Called with interrupts off.
4112 */
4113static struct job *
4114makejob(union node *node, int nprocs)
4115{
4116 int i;
4117 struct job *jp;
4118
4119 for (i = njobs, jp = jobtab; ; jp++) {
4120 if (--i < 0) {
4121 jp = growjobtab();
4122 break;
4123 }
4124 if (jp->used == 0)
4125 break;
4126 if (jp->state != JOBDONE || !jp->waited)
4127 continue;
4128#if JOBS
4129 if (jobctl)
4130 continue;
4131#endif
4132 freejob(jp);
4133 break;
4134 }
4135 memset(jp, 0, sizeof(*jp));
4136#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004137 /* jp->jobctl is a bitfield.
4138 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004139 if (jobctl)
4140 jp->jobctl = 1;
4141#endif
4142 jp->prev_job = curjob;
4143 curjob = jp;
4144 jp->used = 1;
4145 jp->ps = &jp->ps0;
4146 if (nprocs > 1) {
4147 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4148 }
4149 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
4150 jobno(jp)));
4151 return jp;
4152}
4153
4154#if JOBS
4155/*
4156 * Return a string identifying a command (to be printed by the
4157 * jobs command).
4158 */
4159static char *cmdnextc;
4160
4161static void
4162cmdputs(const char *s)
4163{
4164 const char *p, *str;
4165 char c, cc[2] = " ";
4166 char *nextc;
4167 int subtype = 0;
4168 int quoted = 0;
4169 static const char vstype[VSTYPE + 1][4] = {
4170 "", "}", "-", "+", "?", "=",
4171 "%", "%%", "#", "##"
4172 };
4173
4174 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4175 p = s;
4176 while ((c = *p++) != 0) {
4177 str = 0;
4178 switch (c) {
4179 case CTLESC:
4180 c = *p++;
4181 break;
4182 case CTLVAR:
4183 subtype = *p++;
4184 if ((subtype & VSTYPE) == VSLENGTH)
4185 str = "${#";
4186 else
4187 str = "${";
4188 if (!(subtype & VSQUOTE) == !(quoted & 1))
4189 goto dostr;
4190 quoted ^= 1;
4191 c = '"';
4192 break;
4193 case CTLENDVAR:
4194 str = "\"}" + !(quoted & 1);
4195 quoted >>= 1;
4196 subtype = 0;
4197 goto dostr;
4198 case CTLBACKQ:
4199 str = "$(...)";
4200 goto dostr;
4201 case CTLBACKQ+CTLQUOTE:
4202 str = "\"$(...)\"";
4203 goto dostr;
4204#if ENABLE_ASH_MATH_SUPPORT
4205 case CTLARI:
4206 str = "$((";
4207 goto dostr;
4208 case CTLENDARI:
4209 str = "))";
4210 goto dostr;
4211#endif
4212 case CTLQUOTEMARK:
4213 quoted ^= 1;
4214 c = '"';
4215 break;
4216 case '=':
4217 if (subtype == 0)
4218 break;
4219 if ((subtype & VSTYPE) != VSNORMAL)
4220 quoted <<= 1;
4221 str = vstype[subtype & VSTYPE];
4222 if (subtype & VSNUL)
4223 c = ':';
4224 else
4225 goto checkstr;
4226 break;
4227 case '\'':
4228 case '\\':
4229 case '"':
4230 case '$':
4231 /* These can only happen inside quotes */
4232 cc[0] = c;
4233 str = cc;
4234 c = '\\';
4235 break;
4236 default:
4237 break;
4238 }
4239 USTPUTC(c, nextc);
4240 checkstr:
4241 if (!str)
4242 continue;
4243 dostr:
4244 while ((c = *str++)) {
4245 USTPUTC(c, nextc);
4246 }
4247 }
4248 if (quoted & 1) {
4249 USTPUTC('"', nextc);
4250 }
4251 *nextc = 0;
4252 cmdnextc = nextc;
4253}
4254
4255/* cmdtxt() and cmdlist() call each other */
4256static void cmdtxt(union node *n);
4257
4258static void
4259cmdlist(union node *np, int sep)
4260{
4261 for (; np; np = np->narg.next) {
4262 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004263 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 cmdtxt(np);
4265 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004266 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004267 }
4268}
4269
4270static void
4271cmdtxt(union node *n)
4272{
4273 union node *np;
4274 struct nodelist *lp;
4275 const char *p;
4276 char s[2];
4277
4278 if (!n)
4279 return;
4280 switch (n->type) {
4281 default:
4282#if DEBUG
4283 abort();
4284#endif
4285 case NPIPE:
4286 lp = n->npipe.cmdlist;
4287 for (;;) {
4288 cmdtxt(lp->n);
4289 lp = lp->next;
4290 if (!lp)
4291 break;
4292 cmdputs(" | ");
4293 }
4294 break;
4295 case NSEMI:
4296 p = "; ";
4297 goto binop;
4298 case NAND:
4299 p = " && ";
4300 goto binop;
4301 case NOR:
4302 p = " || ";
4303 binop:
4304 cmdtxt(n->nbinary.ch1);
4305 cmdputs(p);
4306 n = n->nbinary.ch2;
4307 goto donode;
4308 case NREDIR:
4309 case NBACKGND:
4310 n = n->nredir.n;
4311 goto donode;
4312 case NNOT:
4313 cmdputs("!");
4314 n = n->nnot.com;
4315 donode:
4316 cmdtxt(n);
4317 break;
4318 case NIF:
4319 cmdputs("if ");
4320 cmdtxt(n->nif.test);
4321 cmdputs("; then ");
4322 n = n->nif.ifpart;
4323 if (n->nif.elsepart) {
4324 cmdtxt(n);
4325 cmdputs("; else ");
4326 n = n->nif.elsepart;
4327 }
4328 p = "; fi";
4329 goto dotail;
4330 case NSUBSHELL:
4331 cmdputs("(");
4332 n = n->nredir.n;
4333 p = ")";
4334 goto dotail;
4335 case NWHILE:
4336 p = "while ";
4337 goto until;
4338 case NUNTIL:
4339 p = "until ";
4340 until:
4341 cmdputs(p);
4342 cmdtxt(n->nbinary.ch1);
4343 n = n->nbinary.ch2;
4344 p = "; done";
4345 dodo:
4346 cmdputs("; do ");
4347 dotail:
4348 cmdtxt(n);
4349 goto dotail2;
4350 case NFOR:
4351 cmdputs("for ");
4352 cmdputs(n->nfor.var);
4353 cmdputs(" in ");
4354 cmdlist(n->nfor.args, 1);
4355 n = n->nfor.body;
4356 p = "; done";
4357 goto dodo;
4358 case NDEFUN:
4359 cmdputs(n->narg.text);
4360 p = "() { ... }";
4361 goto dotail2;
4362 case NCMD:
4363 cmdlist(n->ncmd.args, 1);
4364 cmdlist(n->ncmd.redirect, 0);
4365 break;
4366 case NARG:
4367 p = n->narg.text;
4368 dotail2:
4369 cmdputs(p);
4370 break;
4371 case NHERE:
4372 case NXHERE:
4373 p = "<<...";
4374 goto dotail2;
4375 case NCASE:
4376 cmdputs("case ");
4377 cmdputs(n->ncase.expr->narg.text);
4378 cmdputs(" in ");
4379 for (np = n->ncase.cases; np; np = np->nclist.next) {
4380 cmdtxt(np->nclist.pattern);
4381 cmdputs(") ");
4382 cmdtxt(np->nclist.body);
4383 cmdputs(";; ");
4384 }
4385 p = "esac";
4386 goto dotail2;
4387 case NTO:
4388 p = ">";
4389 goto redir;
4390 case NCLOBBER:
4391 p = ">|";
4392 goto redir;
4393 case NAPPEND:
4394 p = ">>";
4395 goto redir;
4396 case NTOFD:
4397 p = ">&";
4398 goto redir;
4399 case NFROM:
4400 p = "<";
4401 goto redir;
4402 case NFROMFD:
4403 p = "<&";
4404 goto redir;
4405 case NFROMTO:
4406 p = "<>";
4407 redir:
4408 s[0] = n->nfile.fd + '0';
4409 s[1] = '\0';
4410 cmdputs(s);
4411 cmdputs(p);
4412 if (n->type == NTOFD || n->type == NFROMFD) {
4413 s[0] = n->ndup.dupfd + '0';
4414 p = s;
4415 goto dotail2;
4416 }
4417 n = n->nfile.fname;
4418 goto donode;
4419 }
4420}
4421
4422static char *
4423commandtext(union node *n)
4424{
4425 char *name;
4426
4427 STARTSTACKSTR(cmdnextc);
4428 cmdtxt(n);
4429 name = stackblock();
4430 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4431 name, cmdnextc, cmdnextc));
4432 return ckstrdup(name);
4433}
4434#endif /* JOBS */
4435
4436/*
4437 * Fork off a subshell. If we are doing job control, give the subshell its
4438 * own process group. Jp is a job structure that the job is to be added to.
4439 * N is the command that will be evaluated by the child. Both jp and n may
4440 * be NULL. The mode parameter can be one of the following:
4441 * FORK_FG - Fork off a foreground process.
4442 * FORK_BG - Fork off a background process.
4443 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4444 * process group even if job control is on.
4445 *
4446 * When job control is turned off, background processes have their standard
4447 * input redirected to /dev/null (except for the second and later processes
4448 * in a pipeline).
4449 *
4450 * Called with interrupts off.
4451 */
4452/*
4453 * Clear traps on a fork.
4454 */
4455static void
4456clear_traps(void)
4457{
4458 char **tp;
4459
4460 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004461 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004462 INT_OFF;
4463 free(*tp);
4464 *tp = NULL;
4465 if (tp != &trap[0])
4466 setsignal(tp - trap);
4467 INT_ON;
4468 }
4469 }
4470}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004471
4472/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004473static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004474
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004475/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004476static void
4477forkchild(struct job *jp, union node *n, int mode)
4478{
4479 int oldlvl;
4480
4481 TRACE(("Child shell %d\n", getpid()));
4482 oldlvl = shlvl;
4483 shlvl++;
4484
4485 closescript();
4486 clear_traps();
4487#if JOBS
4488 /* do job control only in root shell */
4489 jobctl = 0;
4490 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4491 pid_t pgrp;
4492
4493 if (jp->nprocs == 0)
4494 pgrp = getpid();
4495 else
4496 pgrp = jp->ps[0].pid;
4497 /* This can fail because we are doing it in the parent also */
4498 (void)setpgid(0, pgrp);
4499 if (mode == FORK_FG)
4500 xtcsetpgrp(ttyfd, pgrp);
4501 setsignal(SIGTSTP);
4502 setsignal(SIGTTOU);
4503 } else
4504#endif
4505 if (mode == FORK_BG) {
4506 ignoresig(SIGINT);
4507 ignoresig(SIGQUIT);
4508 if (jp->nprocs == 0) {
4509 close(0);
4510 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004511 ash_msg_and_raise_error("can't open %s", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512 }
4513 }
4514 if (!oldlvl && iflag) {
4515 setsignal(SIGINT);
4516 setsignal(SIGQUIT);
4517 setsignal(SIGTERM);
4518 }
4519 for (jp = curjob; jp; jp = jp->prev_job)
4520 freejob(jp);
4521 jobless = 0;
4522}
4523
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004524/* Called after fork(), in parent */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004525static void
4526forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4527{
4528 TRACE(("In parent shell: child = %d\n", pid));
4529 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004530 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4531 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004532 jobless++;
4533 return;
4534 }
4535#if JOBS
4536 if (mode != FORK_NOJOB && jp->jobctl) {
4537 int pgrp;
4538
4539 if (jp->nprocs == 0)
4540 pgrp = pid;
4541 else
4542 pgrp = jp->ps[0].pid;
4543 /* This can fail because we are doing it in the child also */
4544 setpgid(pid, pgrp);
4545 }
4546#endif
4547 if (mode == FORK_BG) {
4548 backgndpid = pid; /* set $! */
4549 set_curjob(jp, CUR_RUNNING);
4550 }
4551 if (jp) {
4552 struct procstat *ps = &jp->ps[jp->nprocs++];
4553 ps->pid = pid;
4554 ps->status = -1;
4555 ps->cmd = nullstr;
4556#if JOBS
4557 if (jobctl && n)
4558 ps->cmd = commandtext(n);
4559#endif
4560 }
4561}
4562
4563static int
4564forkshell(struct job *jp, union node *n, int mode)
4565{
4566 int pid;
4567
4568 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4569 pid = fork();
4570 if (pid < 0) {
4571 TRACE(("Fork failed, errno=%d", errno));
4572 if (jp)
4573 freejob(jp);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004574 ash_msg_and_raise_error("cannot fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004575 }
4576 if (pid == 0)
4577 forkchild(jp, n, mode);
4578 else
4579 forkparent(jp, n, mode, pid);
4580 return pid;
4581}
4582
4583/*
4584 * Wait for job to finish.
4585 *
4586 * Under job control we have the problem that while a child process is
4587 * running interrupts generated by the user are sent to the child but not
4588 * to the shell. This means that an infinite loop started by an inter-
4589 * active user may be hard to kill. With job control turned off, an
4590 * interactive user may place an interactive program inside a loop. If
4591 * the interactive program catches interrupts, the user doesn't want
4592 * these interrupts to also abort the loop. The approach we take here
4593 * is to have the shell ignore interrupt signals while waiting for a
4594 * foreground process to terminate, and then send itself an interrupt
4595 * signal if the child process was terminated by an interrupt signal.
4596 * Unfortunately, some programs want to do a bit of cleanup and then
4597 * exit on interrupt; unless these processes terminate themselves by
4598 * sending a signal to themselves (instead of calling exit) they will
4599 * confuse this approach.
4600 *
4601 * Called with interrupts off.
4602 */
4603static int
4604waitforjob(struct job *jp)
4605{
4606 int st;
4607
4608 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
4609 while (jp->state == JOBRUNNING) {
4610 dowait(DOWAIT_BLOCK, jp);
4611 }
4612 st = getstatus(jp);
4613#if JOBS
4614 if (jp->jobctl) {
4615 xtcsetpgrp(ttyfd, rootpid);
4616 /*
4617 * This is truly gross.
4618 * If we're doing job control, then we did a TIOCSPGRP which
4619 * caused us (the shell) to no longer be in the controlling
4620 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4621 * intuit from the subprocess exit status whether a SIGINT
4622 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4623 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004624 if (jp->sigint) /* TODO: do the same with all signals */
4625 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004626 }
4627 if (jp->state == JOBDONE)
4628#endif
4629 freejob(jp);
4630 return st;
4631}
4632
4633/*
4634 * return 1 if there are stopped jobs, otherwise 0
4635 */
4636static int
4637stoppedjobs(void)
4638{
4639 struct job *jp;
4640 int retval;
4641
4642 retval = 0;
4643 if (job_warning)
4644 goto out;
4645 jp = curjob;
4646 if (jp && jp->state == JOBSTOPPED) {
4647 out2str("You have stopped jobs.\n");
4648 job_warning = 2;
4649 retval++;
4650 }
4651 out:
4652 return retval;
4653}
4654
4655
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004656/* ============ redir.c
4657 *
4658 * Code for dealing with input/output redirection.
4659 */
4660
4661#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004662#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004663#ifndef PIPE_BUF
4664# define PIPESIZE 4096 /* amount of buffering in a pipe */
4665#else
4666# define PIPESIZE PIPE_BUF
4667#endif
4668
4669/*
4670 * Open a file in noclobber mode.
4671 * The code was copied from bash.
4672 */
4673static int
4674noclobberopen(const char *fname)
4675{
4676 int r, fd;
4677 struct stat finfo, finfo2;
4678
4679 /*
4680 * If the file exists and is a regular file, return an error
4681 * immediately.
4682 */
4683 r = stat(fname, &finfo);
4684 if (r == 0 && S_ISREG(finfo.st_mode)) {
4685 errno = EEXIST;
4686 return -1;
4687 }
4688
4689 /*
4690 * If the file was not present (r != 0), make sure we open it
4691 * exclusively so that if it is created before we open it, our open
4692 * will fail. Make sure that we do not truncate an existing file.
4693 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4694 * file was not a regular file, we leave O_EXCL off.
4695 */
4696 if (r != 0)
4697 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4698 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4699
4700 /* If the open failed, return the file descriptor right away. */
4701 if (fd < 0)
4702 return fd;
4703
4704 /*
4705 * OK, the open succeeded, but the file may have been changed from a
4706 * non-regular file to a regular file between the stat and the open.
4707 * We are assuming that the O_EXCL open handles the case where FILENAME
4708 * did not exist and is symlinked to an existing file between the stat
4709 * and open.
4710 */
4711
4712 /*
4713 * If we can open it and fstat the file descriptor, and neither check
4714 * revealed that it was a regular file, and the file has not been
4715 * replaced, return the file descriptor.
4716 */
4717 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4718 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4719 return fd;
4720
4721 /* The file has been replaced. badness. */
4722 close(fd);
4723 errno = EEXIST;
4724 return -1;
4725}
4726
4727/*
4728 * Handle here documents. Normally we fork off a process to write the
4729 * data to a pipe. If the document is short, we can stuff the data in
4730 * the pipe without forking.
4731 */
4732/* openhere needs this forward reference */
4733static void expandhere(union node *arg, int fd);
4734static int
4735openhere(union node *redir)
4736{
4737 int pip[2];
4738 size_t len = 0;
4739
4740 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004741 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004742 if (redir->type == NHERE) {
4743 len = strlen(redir->nhere.doc->narg.text);
4744 if (len <= PIPESIZE) {
4745 full_write(pip[1], redir->nhere.doc->narg.text, len);
4746 goto out;
4747 }
4748 }
4749 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
4750 close(pip[0]);
4751 signal(SIGINT, SIG_IGN);
4752 signal(SIGQUIT, SIG_IGN);
4753 signal(SIGHUP, SIG_IGN);
4754#ifdef SIGTSTP
4755 signal(SIGTSTP, SIG_IGN);
4756#endif
4757 signal(SIGPIPE, SIG_DFL);
4758 if (redir->type == NHERE)
4759 full_write(pip[1], redir->nhere.doc->narg.text, len);
4760 else
4761 expandhere(redir->nhere.doc, pip[1]);
4762 _exit(0);
4763 }
4764 out:
4765 close(pip[1]);
4766 return pip[0];
4767}
4768
4769static int
4770openredirect(union node *redir)
4771{
4772 char *fname;
4773 int f;
4774
4775 switch (redir->nfile.type) {
4776 case NFROM:
4777 fname = redir->nfile.expfname;
4778 f = open(fname, O_RDONLY);
4779 if (f < 0)
4780 goto eopen;
4781 break;
4782 case NFROMTO:
4783 fname = redir->nfile.expfname;
4784 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4785 if (f < 0)
4786 goto ecreate;
4787 break;
4788 case NTO:
4789 /* Take care of noclobber mode. */
4790 if (Cflag) {
4791 fname = redir->nfile.expfname;
4792 f = noclobberopen(fname);
4793 if (f < 0)
4794 goto ecreate;
4795 break;
4796 }
4797 /* FALLTHROUGH */
4798 case NCLOBBER:
4799 fname = redir->nfile.expfname;
4800 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4801 if (f < 0)
4802 goto ecreate;
4803 break;
4804 case NAPPEND:
4805 fname = redir->nfile.expfname;
4806 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4807 if (f < 0)
4808 goto ecreate;
4809 break;
4810 default:
4811#if DEBUG
4812 abort();
4813#endif
4814 /* Fall through to eliminate warning. */
4815 case NTOFD:
4816 case NFROMFD:
4817 f = -1;
4818 break;
4819 case NHERE:
4820 case NXHERE:
4821 f = openhere(redir);
4822 break;
4823 }
4824
4825 return f;
4826 ecreate:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004827 ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004828 eopen:
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004829 ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004830}
4831
4832/*
4833 * Copy a file descriptor to be >= to. Returns -1
4834 * if the source file descriptor is closed, EMPTY if there are no unused
4835 * file descriptors left.
4836 */
4837static int
4838copyfd(int from, int to)
4839{
4840 int newfd;
4841
4842 newfd = fcntl(from, F_DUPFD, to);
4843 if (newfd < 0) {
4844 if (errno == EMFILE)
4845 return EMPTY;
4846 ash_msg_and_raise_error("%d: %m", from);
4847 }
4848 return newfd;
4849}
4850
4851static void
4852dupredirect(union node *redir, int f)
4853{
4854 int fd = redir->nfile.fd;
4855
4856 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
4857 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
4858 copyfd(redir->ndup.dupfd, fd);
4859 }
4860 return;
4861 }
4862
4863 if (f != fd) {
4864 copyfd(f, fd);
4865 close(f);
4866 }
4867}
4868
4869/*
4870 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
4871 * old file descriptors are stashed away so that the redirection can be
4872 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
4873 * standard output, and the standard error if it becomes a duplicate of
4874 * stdout, is saved in memory.
4875 */
4876/* flags passed to redirect */
4877#define REDIR_PUSH 01 /* save previous values of file descriptors */
4878#define REDIR_SAVEFD2 03 /* set preverrout */
4879static void
4880redirect(union node *redir, int flags)
4881{
4882 union node *n;
4883 struct redirtab *sv;
4884 int i;
4885 int fd;
4886 int newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004887
Denis Vlasenko01631112007-12-16 17:20:38 +00004888 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004889 if (!redir) {
4890 return;
4891 }
4892 sv = NULL;
4893 INT_OFF;
4894 if (flags & REDIR_PUSH) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004895 sv = ckmalloc(sizeof(*sv));
4896 sv->next = redirlist;
4897 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00004898 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004899 for (i = 0; i < 10; i++)
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004900 sv->renamed[i] = EMPTY;
Denis Vlasenko01631112007-12-16 17:20:38 +00004901 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004902 }
4903 n = redir;
4904 do {
4905 fd = n->nfile.fd;
4906 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
4907 && n->ndup.dupfd == fd)
4908 continue; /* redirect from/to same file descriptor */
4909
4910 newfd = openredirect(n);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004911 if (fd == newfd) {
4912 /* Descriptor wasn't open before redirect.
4913 * Mark it for close in the future */
4914 if (sv && sv->renamed[fd] == EMPTY)
4915 sv->renamed[fd] = CLOSED;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004916 continue;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004917 }
4918 if (sv && sv->renamed[fd] == EMPTY) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004919 i = fcntl(fd, F_DUPFD, 10);
4920
4921 if (i == -1) {
4922 i = errno;
4923 if (i != EBADF) {
4924 close(newfd);
4925 errno = i;
4926 ash_msg_and_raise_error("%d: %m", fd);
4927 /* NOTREACHED */
4928 }
4929 } else {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004930 sv->renamed[fd] = i;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004931 close(fd);
4932 }
4933 } else {
4934 close(fd);
4935 }
4936 dupredirect(n, newfd);
4937 } while ((n = n->nfile.next));
4938 INT_ON;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004939 if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004940 preverrout_fd = sv->renamed[2];
4941}
4942
4943/*
4944 * Undo the effects of the last redirection.
4945 */
4946static void
4947popredir(int drop)
4948{
4949 struct redirtab *rp;
4950 int i;
4951
Denis Vlasenko01631112007-12-16 17:20:38 +00004952 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004953 return;
4954 INT_OFF;
4955 rp = redirlist;
4956 for (i = 0; i < 10; i++) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004957 if (rp->renamed[i] == CLOSED) {
4958 if (!drop)
4959 close(i);
4960 continue;
4961 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004962 if (rp->renamed[i] != EMPTY) {
4963 if (!drop) {
4964 close(i);
4965 copyfd(rp->renamed[i], i);
4966 }
4967 close(rp->renamed[i]);
4968 }
4969 }
4970 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00004971 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004972 free(rp);
4973 INT_ON;
4974}
4975
4976/*
4977 * Undo all redirections. Called on error or interrupt.
4978 */
4979
4980/*
4981 * Discard all saved file descriptors.
4982 */
4983static void
4984clearredir(int drop)
4985{
4986 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00004987 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004988 if (!redirlist)
4989 break;
4990 popredir(drop);
4991 }
4992}
4993
4994static int
4995redirectsafe(union node *redir, int flags)
4996{
4997 int err;
4998 volatile int saveint;
4999 struct jmploc *volatile savehandler = exception_handler;
5000 struct jmploc jmploc;
5001
5002 SAVE_INT(saveint);
5003 err = setjmp(jmploc.loc) * 2;
5004 if (!err) {
5005 exception_handler = &jmploc;
5006 redirect(redir, flags);
5007 }
5008 exception_handler = savehandler;
5009 if (err && exception != EXERROR)
5010 longjmp(exception_handler->loc, 1);
5011 RESTORE_INT(saveint);
5012 return err;
5013}
5014
5015
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005016/* ============ Routines to expand arguments to commands
5017 *
5018 * We have to deal with backquotes, shell variables, and file metacharacters.
5019 */
5020
5021/*
5022 * expandarg flags
5023 */
5024#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5025#define EXP_TILDE 0x2 /* do normal tilde expansion */
5026#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5027#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5028#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5029#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5030#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5031#define EXP_WORD 0x80 /* expand word in parameter expansion */
5032#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5033/*
5034 * _rmescape() flags
5035 */
5036#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5037#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5038#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5039#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5040#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5041
5042/*
5043 * Structure specifying which parts of the string should be searched
5044 * for IFS characters.
5045 */
5046struct ifsregion {
5047 struct ifsregion *next; /* next region in list */
5048 int begoff; /* offset of start of region */
5049 int endoff; /* offset of end of region */
5050 int nulonly; /* search for nul bytes only */
5051};
5052
5053struct arglist {
5054 struct strlist *list;
5055 struct strlist **lastp;
5056};
5057
5058/* output of current string */
5059static char *expdest;
5060/* list of back quote expressions */
5061static struct nodelist *argbackq;
5062/* first struct in list of ifs regions */
5063static struct ifsregion ifsfirst;
5064/* last struct in list */
5065static struct ifsregion *ifslastp;
5066/* holds expanded arg list */
5067static struct arglist exparg;
5068
5069/*
5070 * Our own itoa().
5071 */
5072static int
5073cvtnum(arith_t num)
5074{
5075 int len;
5076
5077 expdest = makestrspace(32, expdest);
5078#if ENABLE_ASH_MATH_SUPPORT_64
5079 len = fmtstr(expdest, 32, "%lld", (long long) num);
5080#else
5081 len = fmtstr(expdest, 32, "%ld", num);
5082#endif
5083 STADJUST(len, expdest);
5084 return len;
5085}
5086
5087static size_t
5088esclen(const char *start, const char *p)
5089{
5090 size_t esc = 0;
5091
5092 while (p > start && *--p == CTLESC) {
5093 esc++;
5094 }
5095 return esc;
5096}
5097
5098/*
5099 * Remove any CTLESC characters from a string.
5100 */
5101static char *
5102_rmescapes(char *str, int flag)
5103{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005104 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005105
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005106 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005107 unsigned inquotes;
5108 int notescaped;
5109 int globbing;
5110
5111 p = strpbrk(str, qchars);
5112 if (!p) {
5113 return str;
5114 }
5115 q = p;
5116 r = str;
5117 if (flag & RMESCAPE_ALLOC) {
5118 size_t len = p - str;
5119 size_t fulllen = len + strlen(p) + 1;
5120
5121 if (flag & RMESCAPE_GROW) {
5122 r = makestrspace(fulllen, expdest);
5123 } else if (flag & RMESCAPE_HEAP) {
5124 r = ckmalloc(fulllen);
5125 } else {
5126 r = stalloc(fulllen);
5127 }
5128 q = r;
5129 if (len > 0) {
5130 q = memcpy(q, str, len) + len;
5131 }
5132 }
5133 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5134 globbing = flag & RMESCAPE_GLOB;
5135 notescaped = globbing;
5136 while (*p) {
5137 if (*p == CTLQUOTEMARK) {
5138 inquotes = ~inquotes;
5139 p++;
5140 notescaped = globbing;
5141 continue;
5142 }
5143 if (*p == '\\') {
5144 /* naked back slash */
5145 notescaped = 0;
5146 goto copy;
5147 }
5148 if (*p == CTLESC) {
5149 p++;
5150 if (notescaped && inquotes && *p != '/') {
5151 *q++ = '\\';
5152 }
5153 }
5154 notescaped = globbing;
5155 copy:
5156 *q++ = *p++;
5157 }
5158 *q = '\0';
5159 if (flag & RMESCAPE_GROW) {
5160 expdest = r;
5161 STADJUST(q - r + 1, expdest);
5162 }
5163 return r;
5164}
5165#define rmescapes(p) _rmescapes((p), 0)
5166
5167#define pmatch(a, b) !fnmatch((a), (b), 0)
5168
5169/*
5170 * Prepare a pattern for a expmeta (internal glob(3)) call.
5171 *
5172 * Returns an stalloced string.
5173 */
5174static char *
5175preglob(const char *pattern, int quoted, int flag)
5176{
5177 flag |= RMESCAPE_GLOB;
5178 if (quoted) {
5179 flag |= RMESCAPE_QUOTED;
5180 }
5181 return _rmescapes((char *)pattern, flag);
5182}
5183
5184/*
5185 * Put a string on the stack.
5186 */
5187static void
5188memtodest(const char *p, size_t len, int syntax, int quotes)
5189{
5190 char *q = expdest;
5191
5192 q = makestrspace(len * 2, q);
5193
5194 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005195 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005196 if (!c)
5197 continue;
5198 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5199 USTPUTC(CTLESC, q);
5200 USTPUTC(c, q);
5201 }
5202
5203 expdest = q;
5204}
5205
5206static void
5207strtodest(const char *p, int syntax, int quotes)
5208{
5209 memtodest(p, strlen(p), syntax, quotes);
5210}
5211
5212/*
5213 * Record the fact that we have to scan this region of the
5214 * string for IFS characters.
5215 */
5216static void
5217recordregion(int start, int end, int nulonly)
5218{
5219 struct ifsregion *ifsp;
5220
5221 if (ifslastp == NULL) {
5222 ifsp = &ifsfirst;
5223 } else {
5224 INT_OFF;
5225 ifsp = ckmalloc(sizeof(*ifsp));
5226 ifsp->next = NULL;
5227 ifslastp->next = ifsp;
5228 INT_ON;
5229 }
5230 ifslastp = ifsp;
5231 ifslastp->begoff = start;
5232 ifslastp->endoff = end;
5233 ifslastp->nulonly = nulonly;
5234}
5235
5236static void
5237removerecordregions(int endoff)
5238{
5239 if (ifslastp == NULL)
5240 return;
5241
5242 if (ifsfirst.endoff > endoff) {
5243 while (ifsfirst.next != NULL) {
5244 struct ifsregion *ifsp;
5245 INT_OFF;
5246 ifsp = ifsfirst.next->next;
5247 free(ifsfirst.next);
5248 ifsfirst.next = ifsp;
5249 INT_ON;
5250 }
5251 if (ifsfirst.begoff > endoff)
5252 ifslastp = NULL;
5253 else {
5254 ifslastp = &ifsfirst;
5255 ifsfirst.endoff = endoff;
5256 }
5257 return;
5258 }
5259
5260 ifslastp = &ifsfirst;
5261 while (ifslastp->next && ifslastp->next->begoff < endoff)
5262 ifslastp=ifslastp->next;
5263 while (ifslastp->next != NULL) {
5264 struct ifsregion *ifsp;
5265 INT_OFF;
5266 ifsp = ifslastp->next->next;
5267 free(ifslastp->next);
5268 ifslastp->next = ifsp;
5269 INT_ON;
5270 }
5271 if (ifslastp->endoff > endoff)
5272 ifslastp->endoff = endoff;
5273}
5274
5275static char *
5276exptilde(char *startp, char *p, int flag)
5277{
5278 char c;
5279 char *name;
5280 struct passwd *pw;
5281 const char *home;
5282 int quotes = flag & (EXP_FULL | EXP_CASE);
5283 int startloc;
5284
5285 name = p + 1;
5286
5287 while ((c = *++p) != '\0') {
5288 switch (c) {
5289 case CTLESC:
5290 return startp;
5291 case CTLQUOTEMARK:
5292 return startp;
5293 case ':':
5294 if (flag & EXP_VARTILDE)
5295 goto done;
5296 break;
5297 case '/':
5298 case CTLENDVAR:
5299 goto done;
5300 }
5301 }
5302 done:
5303 *p = '\0';
5304 if (*name == '\0') {
5305 home = lookupvar(homestr);
5306 } else {
5307 pw = getpwnam(name);
5308 if (pw == NULL)
5309 goto lose;
5310 home = pw->pw_dir;
5311 }
5312 if (!home || !*home)
5313 goto lose;
5314 *p = c;
5315 startloc = expdest - (char *)stackblock();
5316 strtodest(home, SQSYNTAX, quotes);
5317 recordregion(startloc, expdest - (char *)stackblock(), 0);
5318 return p;
5319 lose:
5320 *p = c;
5321 return startp;
5322}
5323
5324/*
5325 * Execute a command inside back quotes. If it's a builtin command, we
5326 * want to save its output in a block obtained from malloc. Otherwise
5327 * we fork off a subprocess and get the output of the command via a pipe.
5328 * Should be called with interrupts off.
5329 */
5330struct backcmd { /* result of evalbackcmd */
5331 int fd; /* file descriptor to read from */
5332 char *buf; /* buffer */
5333 int nleft; /* number of chars in buffer */
5334 struct job *jp; /* job structure for command */
5335};
5336
5337/* These forward decls are needed to use "eval" code for backticks handling: */
5338static int back_exitstatus; /* exit status of backquoted command */
5339#define EV_EXIT 01 /* exit after evaluating tree */
5340static void evaltree(union node *, int);
5341
5342static void
5343evalbackcmd(union node *n, struct backcmd *result)
5344{
5345 int saveherefd;
5346
5347 result->fd = -1;
5348 result->buf = NULL;
5349 result->nleft = 0;
5350 result->jp = NULL;
5351 if (n == NULL) {
5352 goto out;
5353 }
5354
5355 saveherefd = herefd;
5356 herefd = -1;
5357
5358 {
5359 int pip[2];
5360 struct job *jp;
5361
5362 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005363 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005364 jp = makejob(n, 1);
5365 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5366 FORCE_INT_ON;
5367 close(pip[0]);
5368 if (pip[1] != 1) {
5369 close(1);
5370 copyfd(pip[1], 1);
5371 close(pip[1]);
5372 }
5373 eflag = 0;
5374 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5375 /* NOTREACHED */
5376 }
5377 close(pip[1]);
5378 result->fd = pip[0];
5379 result->jp = jp;
5380 }
5381 herefd = saveherefd;
5382 out:
5383 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5384 result->fd, result->buf, result->nleft, result->jp));
5385}
5386
5387/*
5388 * Expand stuff in backwards quotes.
5389 */
5390static void
5391expbackq(union node *cmd, int quoted, int quotes)
5392{
5393 struct backcmd in;
5394 int i;
5395 char buf[128];
5396 char *p;
5397 char *dest;
5398 int startloc;
5399 int syntax = quoted? DQSYNTAX : BASESYNTAX;
5400 struct stackmark smark;
5401
5402 INT_OFF;
5403 setstackmark(&smark);
5404 dest = expdest;
5405 startloc = dest - (char *)stackblock();
5406 grabstackstr(dest);
5407 evalbackcmd(cmd, &in);
5408 popstackmark(&smark);
5409
5410 p = in.buf;
5411 i = in.nleft;
5412 if (i == 0)
5413 goto read;
5414 for (;;) {
5415 memtodest(p, i, syntax, quotes);
5416 read:
5417 if (in.fd < 0)
5418 break;
5419 i = safe_read(in.fd, buf, sizeof(buf));
5420 TRACE(("expbackq: read returns %d\n", i));
5421 if (i <= 0)
5422 break;
5423 p = buf;
5424 }
5425
Denis Vlasenko60818682007-09-28 22:07:23 +00005426 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005427 if (in.fd >= 0) {
5428 close(in.fd);
5429 back_exitstatus = waitforjob(in.jp);
5430 }
5431 INT_ON;
5432
5433 /* Eat all trailing newlines */
5434 dest = expdest;
5435 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5436 STUNPUTC(dest);
5437 expdest = dest;
5438
5439 if (quoted == 0)
5440 recordregion(startloc, dest - (char *)stackblock(), 0);
5441 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5442 (dest - (char *)stackblock()) - startloc,
5443 (dest - (char *)stackblock()) - startloc,
5444 stackblock() + startloc));
5445}
5446
5447#if ENABLE_ASH_MATH_SUPPORT
5448/*
5449 * Expand arithmetic expression. Backup to start of expression,
5450 * evaluate, place result in (backed up) result, adjust string position.
5451 */
5452static void
5453expari(int quotes)
5454{
5455 char *p, *start;
5456 int begoff;
5457 int flag;
5458 int len;
5459
5460 /* ifsfree(); */
5461
5462 /*
5463 * This routine is slightly over-complicated for
5464 * efficiency. Next we scan backwards looking for the
5465 * start of arithmetic.
5466 */
5467 start = stackblock();
5468 p = expdest - 1;
5469 *p = '\0';
5470 p--;
5471 do {
5472 int esc;
5473
5474 while (*p != CTLARI) {
5475 p--;
5476#if DEBUG
5477 if (p < start) {
5478 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5479 }
5480#endif
5481 }
5482
5483 esc = esclen(start, p);
5484 if (!(esc % 2)) {
5485 break;
5486 }
5487
5488 p -= esc + 1;
5489 } while (1);
5490
5491 begoff = p - start;
5492
5493 removerecordregions(begoff);
5494
5495 flag = p[1];
5496
5497 expdest = p;
5498
5499 if (quotes)
5500 rmescapes(p + 2);
5501
5502 len = cvtnum(dash_arith(p + 2));
5503
5504 if (flag != '"')
5505 recordregion(begoff, begoff + len, 0);
5506}
5507#endif
5508
5509/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005510static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005511
5512/*
5513 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5514 * characters to allow for further processing. Otherwise treat
5515 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005516 *
5517 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5518 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5519 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005520 */
5521static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005522argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005523{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005524 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005525 '=',
5526 ':',
5527 CTLQUOTEMARK,
5528 CTLENDVAR,
5529 CTLESC,
5530 CTLVAR,
5531 CTLBACKQ,
5532 CTLBACKQ | CTLQUOTE,
5533#if ENABLE_ASH_MATH_SUPPORT
5534 CTLENDARI,
5535#endif
5536 0
5537 };
5538 const char *reject = spclchars;
5539 int c;
5540 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5541 int breakall = flag & EXP_WORD;
5542 int inquotes;
5543 size_t length;
5544 int startloc;
5545
5546 if (!(flag & EXP_VARTILDE)) {
5547 reject += 2;
5548 } else if (flag & EXP_VARTILDE2) {
5549 reject++;
5550 }
5551 inquotes = 0;
5552 length = 0;
5553 if (flag & EXP_TILDE) {
5554 char *q;
5555
5556 flag &= ~EXP_TILDE;
5557 tilde:
5558 q = p;
5559 if (*q == CTLESC && (flag & EXP_QWORD))
5560 q++;
5561 if (*q == '~')
5562 p = exptilde(p, q, flag);
5563 }
5564 start:
5565 startloc = expdest - (char *)stackblock();
5566 for (;;) {
5567 length += strcspn(p + length, reject);
5568 c = p[length];
5569 if (c && (!(c & 0x80)
5570#if ENABLE_ASH_MATH_SUPPORT
5571 || c == CTLENDARI
5572#endif
5573 )) {
5574 /* c == '=' || c == ':' || c == CTLENDARI */
5575 length++;
5576 }
5577 if (length > 0) {
5578 int newloc;
5579 expdest = stack_nputstr(p, length, expdest);
5580 newloc = expdest - (char *)stackblock();
5581 if (breakall && !inquotes && newloc > startloc) {
5582 recordregion(startloc, newloc, 0);
5583 }
5584 startloc = newloc;
5585 }
5586 p += length + 1;
5587 length = 0;
5588
5589 switch (c) {
5590 case '\0':
5591 goto breakloop;
5592 case '=':
5593 if (flag & EXP_VARTILDE2) {
5594 p--;
5595 continue;
5596 }
5597 flag |= EXP_VARTILDE2;
5598 reject++;
5599 /* fall through */
5600 case ':':
5601 /*
5602 * sort of a hack - expand tildes in variable
5603 * assignments (after the first '=' and after ':'s).
5604 */
5605 if (*--p == '~') {
5606 goto tilde;
5607 }
5608 continue;
5609 }
5610
5611 switch (c) {
5612 case CTLENDVAR: /* ??? */
5613 goto breakloop;
5614 case CTLQUOTEMARK:
5615 /* "$@" syntax adherence hack */
5616 if (
5617 !inquotes &&
5618 !memcmp(p, dolatstr, 4) &&
5619 (p[4] == CTLQUOTEMARK || (
5620 p[4] == CTLENDVAR &&
5621 p[5] == CTLQUOTEMARK
5622 ))
5623 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005624 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005625 goto start;
5626 }
5627 inquotes = !inquotes;
5628 addquote:
5629 if (quotes) {
5630 p--;
5631 length++;
5632 startloc++;
5633 }
5634 break;
5635 case CTLESC:
5636 startloc++;
5637 length++;
5638 goto addquote;
5639 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005640 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 goto start;
5642 case CTLBACKQ:
5643 c = 0;
5644 case CTLBACKQ|CTLQUOTE:
5645 expbackq(argbackq->n, c, quotes);
5646 argbackq = argbackq->next;
5647 goto start;
5648#if ENABLE_ASH_MATH_SUPPORT
5649 case CTLENDARI:
5650 p--;
5651 expari(quotes);
5652 goto start;
5653#endif
5654 }
5655 }
5656 breakloop:
5657 ;
5658}
5659
5660static char *
5661scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5662 int zero)
5663{
5664 char *loc;
5665 char *loc2;
5666 char c;
5667
5668 loc = startp;
5669 loc2 = rmesc;
5670 do {
5671 int match;
5672 const char *s = loc2;
5673 c = *loc2;
5674 if (zero) {
5675 *loc2 = '\0';
5676 s = rmesc;
5677 }
5678 match = pmatch(str, s);
5679 *loc2 = c;
5680 if (match)
5681 return loc;
5682 if (quotes && *loc == CTLESC)
5683 loc++;
5684 loc++;
5685 loc2++;
5686 } while (c);
5687 return 0;
5688}
5689
5690static char *
5691scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5692 int zero)
5693{
5694 int esc = 0;
5695 char *loc;
5696 char *loc2;
5697
5698 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5699 int match;
5700 char c = *loc2;
5701 const char *s = loc2;
5702 if (zero) {
5703 *loc2 = '\0';
5704 s = rmesc;
5705 }
5706 match = pmatch(str, s);
5707 *loc2 = c;
5708 if (match)
5709 return loc;
5710 loc--;
5711 if (quotes) {
5712 if (--esc < 0) {
5713 esc = esclen(startp, loc);
5714 }
5715 if (esc % 2) {
5716 esc--;
5717 loc--;
5718 }
5719 }
5720 }
5721 return 0;
5722}
5723
5724static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
5725static void
5726varunset(const char *end, const char *var, const char *umsg, int varflags)
5727{
5728 const char *msg;
5729 const char *tail;
5730
5731 tail = nullstr;
5732 msg = "parameter not set";
5733 if (umsg) {
5734 if (*end == CTLENDVAR) {
5735 if (varflags & VSNUL)
5736 tail = " or null";
5737 } else
5738 msg = umsg;
5739 }
5740 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
5741}
5742
5743static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005744subevalvar(char *p, char *str, int strloc, int subtype,
5745 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005746{
5747 char *startp;
5748 char *loc;
5749 int saveherefd = herefd;
5750 struct nodelist *saveargbackq = argbackq;
5751 int amount;
5752 char *rmesc, *rmescend;
5753 int zero;
5754 char *(*scan)(char *, char *, char *, char *, int , int);
5755
5756 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005757 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
5758 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005759 STPUTC('\0', expdest);
5760 herefd = saveherefd;
5761 argbackq = saveargbackq;
5762 startp = stackblock() + startloc;
5763
5764 switch (subtype) {
5765 case VSASSIGN:
5766 setvar(str, startp, 0);
5767 amount = startp - expdest;
5768 STADJUST(amount, expdest);
5769 return startp;
5770
5771 case VSQUESTION:
5772 varunset(p, str, startp, varflags);
5773 /* NOTREACHED */
5774 }
5775
5776 subtype -= VSTRIMRIGHT;
5777#if DEBUG
5778 if (subtype < 0 || subtype > 3)
5779 abort();
5780#endif
5781
5782 rmesc = startp;
5783 rmescend = stackblock() + strloc;
5784 if (quotes) {
5785 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5786 if (rmesc != startp) {
5787 rmescend = expdest;
5788 startp = stackblock() + startloc;
5789 }
5790 }
5791 rmescend--;
5792 str = stackblock() + strloc;
5793 preglob(str, varflags & VSQUOTE, 0);
5794
5795 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5796 zero = subtype >> 1;
5797 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5798 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5799
5800 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5801 if (loc) {
5802 if (zero) {
5803 memmove(startp, loc, str - loc);
5804 loc = startp + (str - loc) - 1;
5805 }
5806 *loc = '\0';
5807 amount = loc - expdest;
5808 STADJUST(amount, expdest);
5809 }
5810 return loc;
5811}
5812
5813/*
5814 * Add the value of a specialized variable to the stack string.
5815 */
5816static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005817varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005818{
5819 int num;
5820 char *p;
5821 int i;
5822 int sep = 0;
5823 int sepq = 0;
5824 ssize_t len = 0;
5825 char **ap;
5826 int syntax;
5827 int quoted = varflags & VSQUOTE;
5828 int subtype = varflags & VSTYPE;
5829 int quotes = flags & (EXP_FULL | EXP_CASE);
5830
5831 if (quoted && (flags & EXP_FULL))
5832 sep = 1 << CHAR_BIT;
5833
5834 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5835 switch (*name) {
5836 case '$':
5837 num = rootpid;
5838 goto numvar;
5839 case '?':
5840 num = exitstatus;
5841 goto numvar;
5842 case '#':
5843 num = shellparam.nparam;
5844 goto numvar;
5845 case '!':
5846 num = backgndpid;
5847 if (num == 0)
5848 return -1;
5849 numvar:
5850 len = cvtnum(num);
5851 break;
5852 case '-':
5853 p = makestrspace(NOPTS, expdest);
5854 for (i = NOPTS - 1; i >= 0; i--) {
5855 if (optlist[i]) {
5856 USTPUTC(optletters(i), p);
5857 len++;
5858 }
5859 }
5860 expdest = p;
5861 break;
5862 case '@':
5863 if (sep)
5864 goto param;
5865 /* fall through */
5866 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005867 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5869 sepq = 1;
5870 param:
5871 ap = shellparam.p;
5872 if (!ap)
5873 return -1;
5874 while ((p = *ap++)) {
5875 size_t partlen;
5876
5877 partlen = strlen(p);
5878 len += partlen;
5879
5880 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5881 memtodest(p, partlen, syntax, quotes);
5882
5883 if (*ap && sep) {
5884 char *q;
5885
5886 len++;
5887 if (subtype == VSPLUS || subtype == VSLENGTH) {
5888 continue;
5889 }
5890 q = expdest;
5891 if (sepq)
5892 STPUTC(CTLESC, q);
5893 STPUTC(sep, q);
5894 expdest = q;
5895 }
5896 }
5897 return len;
5898 case '0':
5899 case '1':
5900 case '2':
5901 case '3':
5902 case '4':
5903 case '5':
5904 case '6':
5905 case '7':
5906 case '8':
5907 case '9':
5908 num = atoi(name);
5909 if (num < 0 || num > shellparam.nparam)
5910 return -1;
5911 p = num ? shellparam.p[num - 1] : arg0;
5912 goto value;
5913 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005914 /* NB: name has form "VAR=..." */
5915
5916 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
5917 * which should be considered before we check variables. */
5918 if (var_str_list) {
5919 unsigned name_len = (strchrnul(name, '=') - name) + 1;
5920 p = NULL;
5921 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00005922 char *str, *eq;
5923 str = var_str_list->text;
5924 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005925 if (!eq) /* stop at first non-assignment */
5926 break;
5927 eq++;
5928 if (name_len == (eq - str)
5929 && strncmp(str, name, name_len) == 0) {
5930 p = eq;
5931 /* goto value; - WRONG! */
5932 /* think "A=1 A=2 B=$A" */
5933 }
5934 var_str_list = var_str_list->next;
5935 } while (var_str_list);
5936 if (p)
5937 goto value;
5938 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005939 p = lookupvar(name);
5940 value:
5941 if (!p)
5942 return -1;
5943
5944 len = strlen(p);
5945 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5946 memtodest(p, len, syntax, quotes);
5947 return len;
5948 }
5949
5950 if (subtype == VSPLUS || subtype == VSLENGTH)
5951 STADJUST(-len, expdest);
5952 return len;
5953}
5954
5955/*
5956 * Expand a variable, and return a pointer to the next character in the
5957 * input string.
5958 */
5959static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005960evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005961{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005962 char varflags;
5963 char subtype;
5964 char quoted;
5965 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005966 char *var;
5967 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005968 int startloc;
5969 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005970
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971 varflags = *p++;
5972 subtype = varflags & VSTYPE;
5973 quoted = varflags & VSQUOTE;
5974 var = p;
5975 easy = (!quoted || (*var == '@' && shellparam.nparam));
5976 startloc = expdest - (char *)stackblock();
5977 p = strchr(p, '=') + 1;
5978
5979 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005980 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981 if (varflags & VSNUL)
5982 varlen--;
5983
5984 if (subtype == VSPLUS) {
5985 varlen = -1 - varlen;
5986 goto vsplus;
5987 }
5988
5989 if (subtype == VSMINUS) {
5990 vsplus:
5991 if (varlen < 0) {
5992 argstr(
5993 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005994 (quoted ? EXP_QWORD : EXP_WORD),
5995 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996 );
5997 goto end;
5998 }
5999 if (easy)
6000 goto record;
6001 goto end;
6002 }
6003
6004 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6005 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006006 if (subevalvar(p, var, /* strloc: */ 0,
6007 subtype, startloc, varflags,
6008 /* quotes: */ 0,
6009 var_str_list)
6010 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006011 varflags &= ~VSNUL;
6012 /*
6013 * Remove any recorded regions beyond
6014 * start of variable
6015 */
6016 removerecordregions(startloc);
6017 goto again;
6018 }
6019 goto end;
6020 }
6021 if (easy)
6022 goto record;
6023 goto end;
6024 }
6025
6026 if (varlen < 0 && uflag)
6027 varunset(p, var, 0, 0);
6028
6029 if (subtype == VSLENGTH) {
6030 cvtnum(varlen > 0 ? varlen : 0);
6031 goto record;
6032 }
6033
6034 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006035 if (easy)
6036 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037 goto end;
6038 }
6039
6040#if DEBUG
6041 switch (subtype) {
6042 case VSTRIMLEFT:
6043 case VSTRIMLEFTMAX:
6044 case VSTRIMRIGHT:
6045 case VSTRIMRIGHTMAX:
6046 break;
6047 default:
6048 abort();
6049 }
6050#endif
6051
6052 if (varlen >= 0) {
6053 /*
6054 * Terminate the string and start recording the pattern
6055 * right after it
6056 */
6057 STPUTC('\0', expdest);
6058 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006059 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6060 startloc, varflags,
6061 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6062 var_str_list)
6063 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006064 int amount = expdest - (
6065 (char *)stackblock() + patloc - 1
6066 );
6067 STADJUST(-amount, expdest);
6068 }
6069 /* Remove any recorded regions beyond start of variable */
6070 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006071 record:
6072 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006073 }
6074
6075 end:
6076 if (subtype != VSNORMAL) { /* skip to end of alternative */
6077 int nesting = 1;
6078 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006079 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006080 if (c == CTLESC)
6081 p++;
6082 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6083 if (varlen >= 0)
6084 argbackq = argbackq->next;
6085 } else if (c == CTLVAR) {
6086 if ((*p++ & VSTYPE) != VSNORMAL)
6087 nesting++;
6088 } else if (c == CTLENDVAR) {
6089 if (--nesting == 0)
6090 break;
6091 }
6092 }
6093 }
6094 return p;
6095}
6096
6097/*
6098 * Break the argument string into pieces based upon IFS and add the
6099 * strings to the argument list. The regions of the string to be
6100 * searched for IFS characters have been stored by recordregion.
6101 */
6102static void
6103ifsbreakup(char *string, struct arglist *arglist)
6104{
6105 struct ifsregion *ifsp;
6106 struct strlist *sp;
6107 char *start;
6108 char *p;
6109 char *q;
6110 const char *ifs, *realifs;
6111 int ifsspc;
6112 int nulonly;
6113
6114 start = string;
6115 if (ifslastp != NULL) {
6116 ifsspc = 0;
6117 nulonly = 0;
6118 realifs = ifsset() ? ifsval() : defifs;
6119 ifsp = &ifsfirst;
6120 do {
6121 p = string + ifsp->begoff;
6122 nulonly = ifsp->nulonly;
6123 ifs = nulonly ? nullstr : realifs;
6124 ifsspc = 0;
6125 while (p < string + ifsp->endoff) {
6126 q = p;
6127 if (*p == CTLESC)
6128 p++;
6129 if (!strchr(ifs, *p)) {
6130 p++;
6131 continue;
6132 }
6133 if (!nulonly)
6134 ifsspc = (strchr(defifs, *p) != NULL);
6135 /* Ignore IFS whitespace at start */
6136 if (q == start && ifsspc) {
6137 p++;
6138 start = p;
6139 continue;
6140 }
6141 *q = '\0';
6142 sp = stalloc(sizeof(*sp));
6143 sp->text = start;
6144 *arglist->lastp = sp;
6145 arglist->lastp = &sp->next;
6146 p++;
6147 if (!nulonly) {
6148 for (;;) {
6149 if (p >= string + ifsp->endoff) {
6150 break;
6151 }
6152 q = p;
6153 if (*p == CTLESC)
6154 p++;
6155 if (strchr(ifs, *p) == NULL ) {
6156 p = q;
6157 break;
6158 } else if (strchr(defifs, *p) == NULL) {
6159 if (ifsspc) {
6160 p++;
6161 ifsspc = 0;
6162 } else {
6163 p = q;
6164 break;
6165 }
6166 } else
6167 p++;
6168 }
6169 }
6170 start = p;
6171 } /* while */
6172 ifsp = ifsp->next;
6173 } while (ifsp != NULL);
6174 if (nulonly)
6175 goto add;
6176 }
6177
6178 if (!*start)
6179 return;
6180
6181 add:
6182 sp = stalloc(sizeof(*sp));
6183 sp->text = start;
6184 *arglist->lastp = sp;
6185 arglist->lastp = &sp->next;
6186}
6187
6188static void
6189ifsfree(void)
6190{
6191 struct ifsregion *p;
6192
6193 INT_OFF;
6194 p = ifsfirst.next;
6195 do {
6196 struct ifsregion *ifsp;
6197 ifsp = p->next;
6198 free(p);
6199 p = ifsp;
6200 } while (p);
6201 ifslastp = NULL;
6202 ifsfirst.next = NULL;
6203 INT_ON;
6204}
6205
6206/*
6207 * Add a file name to the list.
6208 */
6209static void
6210addfname(const char *name)
6211{
6212 struct strlist *sp;
6213
6214 sp = stalloc(sizeof(*sp));
6215 sp->text = ststrdup(name);
6216 *exparg.lastp = sp;
6217 exparg.lastp = &sp->next;
6218}
6219
6220static char *expdir;
6221
6222/*
6223 * Do metacharacter (i.e. *, ?, [...]) expansion.
6224 */
6225static void
6226expmeta(char *enddir, char *name)
6227{
6228 char *p;
6229 const char *cp;
6230 char *start;
6231 char *endname;
6232 int metaflag;
6233 struct stat statb;
6234 DIR *dirp;
6235 struct dirent *dp;
6236 int atend;
6237 int matchdot;
6238
6239 metaflag = 0;
6240 start = name;
6241 for (p = name; *p; p++) {
6242 if (*p == '*' || *p == '?')
6243 metaflag = 1;
6244 else if (*p == '[') {
6245 char *q = p + 1;
6246 if (*q == '!')
6247 q++;
6248 for (;;) {
6249 if (*q == '\\')
6250 q++;
6251 if (*q == '/' || *q == '\0')
6252 break;
6253 if (*++q == ']') {
6254 metaflag = 1;
6255 break;
6256 }
6257 }
6258 } else if (*p == '\\')
6259 p++;
6260 else if (*p == '/') {
6261 if (metaflag)
6262 goto out;
6263 start = p + 1;
6264 }
6265 }
6266 out:
6267 if (metaflag == 0) { /* we've reached the end of the file name */
6268 if (enddir != expdir)
6269 metaflag++;
6270 p = name;
6271 do {
6272 if (*p == '\\')
6273 p++;
6274 *enddir++ = *p;
6275 } while (*p++);
6276 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6277 addfname(expdir);
6278 return;
6279 }
6280 endname = p;
6281 if (name < start) {
6282 p = name;
6283 do {
6284 if (*p == '\\')
6285 p++;
6286 *enddir++ = *p++;
6287 } while (p < start);
6288 }
6289 if (enddir == expdir) {
6290 cp = ".";
6291 } else if (enddir == expdir + 1 && *expdir == '/') {
6292 cp = "/";
6293 } else {
6294 cp = expdir;
6295 enddir[-1] = '\0';
6296 }
6297 dirp = opendir(cp);
6298 if (dirp == NULL)
6299 return;
6300 if (enddir != expdir)
6301 enddir[-1] = '/';
6302 if (*endname == 0) {
6303 atend = 1;
6304 } else {
6305 atend = 0;
6306 *endname++ = '\0';
6307 }
6308 matchdot = 0;
6309 p = start;
6310 if (*p == '\\')
6311 p++;
6312 if (*p == '.')
6313 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006314 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006315 if (dp->d_name[0] == '.' && ! matchdot)
6316 continue;
6317 if (pmatch(start, dp->d_name)) {
6318 if (atend) {
6319 strcpy(enddir, dp->d_name);
6320 addfname(expdir);
6321 } else {
6322 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6323 continue;
6324 p[-1] = '/';
6325 expmeta(p, endname);
6326 }
6327 }
6328 }
6329 closedir(dirp);
6330 if (! atend)
6331 endname[-1] = '/';
6332}
6333
6334static struct strlist *
6335msort(struct strlist *list, int len)
6336{
6337 struct strlist *p, *q = NULL;
6338 struct strlist **lpp;
6339 int half;
6340 int n;
6341
6342 if (len <= 1)
6343 return list;
6344 half = len >> 1;
6345 p = list;
6346 for (n = half; --n >= 0; ) {
6347 q = p;
6348 p = p->next;
6349 }
6350 q->next = NULL; /* terminate first half of list */
6351 q = msort(list, half); /* sort first half of list */
6352 p = msort(p, len - half); /* sort second half */
6353 lpp = &list;
6354 for (;;) {
6355#if ENABLE_LOCALE_SUPPORT
6356 if (strcoll(p->text, q->text) < 0)
6357#else
6358 if (strcmp(p->text, q->text) < 0)
6359#endif
6360 {
6361 *lpp = p;
6362 lpp = &p->next;
6363 p = *lpp;
6364 if (p == NULL) {
6365 *lpp = q;
6366 break;
6367 }
6368 } else {
6369 *lpp = q;
6370 lpp = &q->next;
6371 q = *lpp;
6372 if (q == NULL) {
6373 *lpp = p;
6374 break;
6375 }
6376 }
6377 }
6378 return list;
6379}
6380
6381/*
6382 * Sort the results of file name expansion. It calculates the number of
6383 * strings to sort and then calls msort (short for merge sort) to do the
6384 * work.
6385 */
6386static struct strlist *
6387expsort(struct strlist *str)
6388{
6389 int len;
6390 struct strlist *sp;
6391
6392 len = 0;
6393 for (sp = str; sp; sp = sp->next)
6394 len++;
6395 return msort(str, len);
6396}
6397
6398static void
6399expandmeta(struct strlist *str, int flag)
6400{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006401 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006402 '*', '?', '[', 0
6403 };
6404 /* TODO - EXP_REDIR */
6405
6406 while (str) {
6407 struct strlist **savelastp;
6408 struct strlist *sp;
6409 char *p;
6410
6411 if (fflag)
6412 goto nometa;
6413 if (!strpbrk(str->text, metachars))
6414 goto nometa;
6415 savelastp = exparg.lastp;
6416
6417 INT_OFF;
6418 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6419 {
6420 int i = strlen(str->text);
6421 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6422 }
6423
6424 expmeta(expdir, p);
6425 free(expdir);
6426 if (p != str->text)
6427 free(p);
6428 INT_ON;
6429 if (exparg.lastp == savelastp) {
6430 /*
6431 * no matches
6432 */
6433 nometa:
6434 *exparg.lastp = str;
6435 rmescapes(str->text);
6436 exparg.lastp = &str->next;
6437 } else {
6438 *exparg.lastp = NULL;
6439 *savelastp = sp = expsort(*savelastp);
6440 while (sp->next != NULL)
6441 sp = sp->next;
6442 exparg.lastp = &sp->next;
6443 }
6444 str = str->next;
6445 }
6446}
6447
6448/*
6449 * Perform variable substitution and command substitution on an argument,
6450 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6451 * perform splitting and file name expansion. When arglist is NULL, perform
6452 * here document expansion.
6453 */
6454static void
6455expandarg(union node *arg, struct arglist *arglist, int flag)
6456{
6457 struct strlist *sp;
6458 char *p;
6459
6460 argbackq = arg->narg.backquote;
6461 STARTSTACKSTR(expdest);
6462 ifsfirst.next = NULL;
6463 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006464 argstr(arg->narg.text, flag,
6465 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006466 p = _STPUTC('\0', expdest);
6467 expdest = p - 1;
6468 if (arglist == NULL) {
6469 return; /* here document expanded */
6470 }
6471 p = grabstackstr(p);
6472 exparg.lastp = &exparg.list;
6473 /*
6474 * TODO - EXP_REDIR
6475 */
6476 if (flag & EXP_FULL) {
6477 ifsbreakup(p, &exparg);
6478 *exparg.lastp = NULL;
6479 exparg.lastp = &exparg.list;
6480 expandmeta(exparg.list, flag);
6481 } else {
6482 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6483 rmescapes(p);
6484 sp = stalloc(sizeof(*sp));
6485 sp->text = p;
6486 *exparg.lastp = sp;
6487 exparg.lastp = &sp->next;
6488 }
6489 if (ifsfirst.next)
6490 ifsfree();
6491 *exparg.lastp = NULL;
6492 if (exparg.list) {
6493 *arglist->lastp = exparg.list;
6494 arglist->lastp = exparg.lastp;
6495 }
6496}
6497
6498/*
6499 * Expand shell variables and backquotes inside a here document.
6500 */
6501static void
6502expandhere(union node *arg, int fd)
6503{
6504 herefd = fd;
6505 expandarg(arg, (struct arglist *)NULL, 0);
6506 full_write(fd, stackblock(), expdest - (char *)stackblock());
6507}
6508
6509/*
6510 * Returns true if the pattern matches the string.
6511 */
6512static int
6513patmatch(char *pattern, const char *string)
6514{
6515 return pmatch(preglob(pattern, 0, 0), string);
6516}
6517
6518/*
6519 * See if a pattern matches in a case statement.
6520 */
6521static int
6522casematch(union node *pattern, char *val)
6523{
6524 struct stackmark smark;
6525 int result;
6526
6527 setstackmark(&smark);
6528 argbackq = pattern->narg.backquote;
6529 STARTSTACKSTR(expdest);
6530 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006531 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
6532 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006533 STACKSTRNUL(expdest);
6534 result = patmatch(stackblock(), val);
6535 popstackmark(&smark);
6536 return result;
6537}
6538
6539
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006540/* ============ find_command */
6541
6542struct builtincmd {
6543 const char *name;
6544 int (*builtin)(int, char **);
6545 /* unsigned flags; */
6546};
6547#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00006548/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006549 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006550#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006551#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006552
6553struct cmdentry {
6554 int cmdtype;
6555 union param {
6556 int index;
6557 const struct builtincmd *cmd;
6558 struct funcnode *func;
6559 } u;
6560};
6561/* values of cmdtype */
6562#define CMDUNKNOWN -1 /* no entry in table for command */
6563#define CMDNORMAL 0 /* command is an executable program */
6564#define CMDFUNCTION 1 /* command is a shell function */
6565#define CMDBUILTIN 2 /* command is a shell builtin */
6566
6567/* action to find_command() */
6568#define DO_ERR 0x01 /* prints errors */
6569#define DO_ABS 0x02 /* checks absolute paths */
6570#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
6571#define DO_ALTPATH 0x08 /* using alternate path */
6572#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
6573
6574static void find_command(char *, struct cmdentry *, int, const char *);
6575
6576
6577/* ============ Hashing commands */
6578
6579/*
6580 * When commands are first encountered, they are entered in a hash table.
6581 * This ensures that a full path search will not have to be done for them
6582 * on each invocation.
6583 *
6584 * We should investigate converting to a linear search, even though that
6585 * would make the command name "hash" a misnomer.
6586 */
6587
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006588#define ARB 1 /* actual size determined at run time */
6589
6590struct tblentry {
6591 struct tblentry *next; /* next entry in hash chain */
6592 union param param; /* definition of builtin function */
6593 short cmdtype; /* index identifying command */
6594 char rehash; /* if set, cd done since entry created */
6595 char cmdname[ARB]; /* name of command */
6596};
6597
Denis Vlasenko01631112007-12-16 17:20:38 +00006598static struct tblentry **cmdtable;
6599#define INIT_G_cmdtable() do { \
6600 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
6601} while (0)
6602
6603static int builtinloc = -1; /* index in path of %builtin, or -1 */
6604
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006605
6606static void
6607tryexec(char *cmd, char **argv, char **envp)
6608{
6609 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006610
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006611#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006612 if (strchr(cmd, '/') == NULL) {
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006613 int a = find_applet_by_name(cmd);
6614 if (a >= 0) {
6615 if (APPLET_IS_NOEXEC(a))
6616 run_applet_no_and_exit(a, argv);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006617 /* re-exec ourselves with the new arguments */
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +00006618 execve(bb_busybox_exec_path, argv, envp);
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006619 /* If they called chroot or otherwise made the binary no longer
6620 * executable, fall through */
6621 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006622 }
6623#endif
6624
6625 repeat:
6626#ifdef SYSV
6627 do {
6628 execve(cmd, argv, envp);
6629 } while (errno == EINTR);
6630#else
6631 execve(cmd, argv, envp);
6632#endif
6633 if (repeated++) {
6634 free(argv);
6635 } else if (errno == ENOEXEC) {
6636 char **ap;
6637 char **new;
6638
6639 for (ap = argv; *ap; ap++)
6640 ;
6641 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
6642 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00006643 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006644 ap += 2;
6645 argv++;
6646 while ((*ap++ = *argv++))
6647 ;
6648 argv = new;
6649 goto repeat;
6650 }
6651}
6652
6653/*
6654 * Exec a program. Never returns. If you change this routine, you may
6655 * have to change the find_command routine as well.
6656 */
6657#define environment() listvars(VEXPORT, VUNSET, 0)
6658static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
6659static void
6660shellexec(char **argv, const char *path, int idx)
6661{
6662 char *cmdname;
6663 int e;
6664 char **envp;
6665 int exerrno;
6666
6667 clearredir(1);
6668 envp = environment();
Denis Vlasenko29e31dd2007-03-03 23:12:17 +00006669 if (strchr(argv[0], '/')
Denis Vlasenko80d14be2007-04-10 23:03:30 +00006670#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +00006671 || find_applet_by_name(argv[0]) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006672#endif
6673 ) {
6674 tryexec(argv[0], argv, envp);
6675 e = errno;
6676 } else {
6677 e = ENOENT;
6678 while ((cmdname = padvance(&path, argv[0])) != NULL) {
6679 if (--idx < 0 && pathopt == NULL) {
6680 tryexec(cmdname, argv, envp);
6681 if (errno != ENOENT && errno != ENOTDIR)
6682 e = errno;
6683 }
6684 stunalloc(cmdname);
6685 }
6686 }
6687
6688 /* Map to POSIX errors */
6689 switch (e) {
6690 case EACCES:
6691 exerrno = 126;
6692 break;
6693 case ENOENT:
6694 exerrno = 127;
6695 break;
6696 default:
6697 exerrno = 2;
6698 break;
6699 }
6700 exitstatus = exerrno;
6701 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
6702 argv[0], e, suppressint ));
6703 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
6704 /* NOTREACHED */
6705}
6706
6707static void
6708printentry(struct tblentry *cmdp)
6709{
6710 int idx;
6711 const char *path;
6712 char *name;
6713
6714 idx = cmdp->param.index;
6715 path = pathval();
6716 do {
6717 name = padvance(&path, cmdp->cmdname);
6718 stunalloc(name);
6719 } while (--idx >= 0);
6720 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
6721}
6722
6723/*
6724 * Clear out command entries. The argument specifies the first entry in
6725 * PATH which has changed.
6726 */
6727static void
6728clearcmdentry(int firstchange)
6729{
6730 struct tblentry **tblp;
6731 struct tblentry **pp;
6732 struct tblentry *cmdp;
6733
6734 INT_OFF;
6735 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
6736 pp = tblp;
6737 while ((cmdp = *pp) != NULL) {
6738 if ((cmdp->cmdtype == CMDNORMAL &&
6739 cmdp->param.index >= firstchange)
6740 || (cmdp->cmdtype == CMDBUILTIN &&
6741 builtinloc >= firstchange)
6742 ) {
6743 *pp = cmdp->next;
6744 free(cmdp);
6745 } else {
6746 pp = &cmdp->next;
6747 }
6748 }
6749 }
6750 INT_ON;
6751}
6752
6753/*
6754 * Locate a command in the command hash table. If "add" is nonzero,
6755 * add the command to the table if it is not already present. The
6756 * variable "lastcmdentry" is set to point to the address of the link
6757 * pointing to the entry, so that delete_cmd_entry can delete the
6758 * entry.
6759 *
6760 * Interrupts must be off if called with add != 0.
6761 */
6762static struct tblentry **lastcmdentry;
6763
6764static struct tblentry *
6765cmdlookup(const char *name, int add)
6766{
6767 unsigned int hashval;
6768 const char *p;
6769 struct tblentry *cmdp;
6770 struct tblentry **pp;
6771
6772 p = name;
6773 hashval = (unsigned char)*p << 4;
6774 while (*p)
6775 hashval += (unsigned char)*p++;
6776 hashval &= 0x7FFF;
6777 pp = &cmdtable[hashval % CMDTABLESIZE];
6778 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6779 if (strcmp(cmdp->cmdname, name) == 0)
6780 break;
6781 pp = &cmdp->next;
6782 }
6783 if (add && cmdp == NULL) {
6784 cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
6785 + strlen(name) + 1);
6786 cmdp->next = NULL;
6787 cmdp->cmdtype = CMDUNKNOWN;
6788 strcpy(cmdp->cmdname, name);
6789 }
6790 lastcmdentry = pp;
6791 return cmdp;
6792}
6793
6794/*
6795 * Delete the command entry returned on the last lookup.
6796 */
6797static void
6798delete_cmd_entry(void)
6799{
6800 struct tblentry *cmdp;
6801
6802 INT_OFF;
6803 cmdp = *lastcmdentry;
6804 *lastcmdentry = cmdp->next;
6805 if (cmdp->cmdtype == CMDFUNCTION)
6806 freefunc(cmdp->param.func);
6807 free(cmdp);
6808 INT_ON;
6809}
6810
6811/*
6812 * Add a new command entry, replacing any existing command entry for
6813 * the same name - except special builtins.
6814 */
6815static void
6816addcmdentry(char *name, struct cmdentry *entry)
6817{
6818 struct tblentry *cmdp;
6819
6820 cmdp = cmdlookup(name, 1);
6821 if (cmdp->cmdtype == CMDFUNCTION) {
6822 freefunc(cmdp->param.func);
6823 }
6824 cmdp->cmdtype = entry->cmdtype;
6825 cmdp->param = entry->u;
6826 cmdp->rehash = 0;
6827}
6828
6829static int
6830hashcmd(int argc, char **argv)
6831{
6832 struct tblentry **pp;
6833 struct tblentry *cmdp;
6834 int c;
6835 struct cmdentry entry;
6836 char *name;
6837
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006838 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006839 clearcmdentry(0);
6840 return 0;
6841 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006842
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006843 if (*argptr == NULL) {
6844 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6845 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
6846 if (cmdp->cmdtype == CMDNORMAL)
6847 printentry(cmdp);
6848 }
6849 }
6850 return 0;
6851 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006852
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006853 c = 0;
6854 while ((name = *argptr) != NULL) {
6855 cmdp = cmdlookup(name, 0);
6856 if (cmdp != NULL
6857 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006858 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
6859 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006860 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006861 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006862 find_command(name, &entry, DO_ERR, pathval());
6863 if (entry.cmdtype == CMDUNKNOWN)
6864 c = 1;
6865 argptr++;
6866 }
6867 return c;
6868}
6869
6870/*
6871 * Called when a cd is done. Marks all commands so the next time they
6872 * are executed they will be rehashed.
6873 */
6874static void
6875hashcd(void)
6876{
6877 struct tblentry **pp;
6878 struct tblentry *cmdp;
6879
6880 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
6881 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006882 if (cmdp->cmdtype == CMDNORMAL
6883 || (cmdp->cmdtype == CMDBUILTIN
6884 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
6885 && builtinloc > 0)
6886 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006887 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006888 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006889 }
6890 }
6891}
6892
6893/*
6894 * Fix command hash table when PATH changed.
6895 * Called before PATH is changed. The argument is the new value of PATH;
6896 * pathval() still returns the old value at this point.
6897 * Called with interrupts off.
6898 */
6899static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006900changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006901{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006902 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006903 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006904 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006905 int idx_bltin;
6906
6907 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006908 firstchange = 9999; /* assume no change */
6909 idx = 0;
6910 idx_bltin = -1;
6911 for (;;) {
6912 if (*old != *new) {
6913 firstchange = idx;
6914 if ((*old == '\0' && *new == ':')
6915 || (*old == ':' && *new == '\0'))
6916 firstchange++;
6917 old = new; /* ignore subsequent differences */
6918 }
6919 if (*new == '\0')
6920 break;
6921 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
6922 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00006923 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006924 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00006925 new++, old++;
6926 }
6927 if (builtinloc < 0 && idx_bltin >= 0)
6928 builtinloc = idx_bltin; /* zap builtins */
6929 if (builtinloc >= 0 && idx_bltin < 0)
6930 firstchange = 0;
6931 clearcmdentry(firstchange);
6932 builtinloc = idx_bltin;
6933}
6934
6935#define TEOF 0
6936#define TNL 1
6937#define TREDIR 2
6938#define TWORD 3
6939#define TSEMI 4
6940#define TBACKGND 5
6941#define TAND 6
6942#define TOR 7
6943#define TPIPE 8
6944#define TLP 9
6945#define TRP 10
6946#define TENDCASE 11
6947#define TENDBQUOTE 12
6948#define TNOT 13
6949#define TCASE 14
6950#define TDO 15
6951#define TDONE 16
6952#define TELIF 17
6953#define TELSE 18
6954#define TESAC 19
6955#define TFI 20
6956#define TFOR 21
6957#define TIF 22
6958#define TIN 23
6959#define TTHEN 24
6960#define TUNTIL 25
6961#define TWHILE 26
6962#define TBEGIN 27
6963#define TEND 28
6964
6965/* first char is indicating which tokens mark the end of a list */
6966static const char *const tokname_array[] = {
6967 "\1end of file",
6968 "\0newline",
6969 "\0redirection",
6970 "\0word",
6971 "\0;",
6972 "\0&",
6973 "\0&&",
6974 "\0||",
6975 "\0|",
6976 "\0(",
6977 "\1)",
6978 "\1;;",
6979 "\1`",
6980#define KWDOFFSET 13
6981 /* the following are keywords */
6982 "\0!",
6983 "\0case",
6984 "\1do",
6985 "\1done",
6986 "\1elif",
6987 "\1else",
6988 "\1esac",
6989 "\1fi",
6990 "\0for",
6991 "\0if",
6992 "\0in",
6993 "\1then",
6994 "\0until",
6995 "\0while",
6996 "\0{",
6997 "\1}",
6998};
6999
7000static const char *
7001tokname(int tok)
7002{
7003 static char buf[16];
7004
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007005//try this:
7006//if (tok < TSEMI) return tokname_array[tok] + 1;
7007//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7008//return buf;
7009
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007010 if (tok >= TSEMI)
7011 buf[0] = '"';
7012 sprintf(buf + (tok >= TSEMI), "%s%c",
7013 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7014 return buf;
7015}
7016
7017/* Wrapper around strcmp for qsort/bsearch/... */
7018static int
7019pstrcmp(const void *a, const void *b)
7020{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007021 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007022}
7023
7024static const char *const *
7025findkwd(const char *s)
7026{
7027 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007028 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7029 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007030}
7031
7032/*
7033 * Locate and print what a word is...
7034 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007035static int
7036describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007037{
7038 struct cmdentry entry;
7039 struct tblentry *cmdp;
7040#if ENABLE_ASH_ALIAS
7041 const struct alias *ap;
7042#endif
7043 const char *path = pathval();
7044
7045 if (describe_command_verbose) {
7046 out1str(command);
7047 }
7048
7049 /* First look at the keywords */
7050 if (findkwd(command)) {
7051 out1str(describe_command_verbose ? " is a shell keyword" : command);
7052 goto out;
7053 }
7054
7055#if ENABLE_ASH_ALIAS
7056 /* Then look at the aliases */
7057 ap = lookupalias(command, 0);
7058 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007059 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007060 out1str("alias ");
7061 printalias(ap);
7062 return 0;
7063 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007064 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007065 goto out;
7066 }
7067#endif
7068 /* Then check if it is a tracked alias */
7069 cmdp = cmdlookup(command, 0);
7070 if (cmdp != NULL) {
7071 entry.cmdtype = cmdp->cmdtype;
7072 entry.u = cmdp->param;
7073 } else {
7074 /* Finally use brute force */
7075 find_command(command, &entry, DO_ABS, path);
7076 }
7077
7078 switch (entry.cmdtype) {
7079 case CMDNORMAL: {
7080 int j = entry.u.index;
7081 char *p;
7082 if (j == -1) {
7083 p = command;
7084 } else {
7085 do {
7086 p = padvance(&path, command);
7087 stunalloc(p);
7088 } while (--j >= 0);
7089 }
7090 if (describe_command_verbose) {
7091 out1fmt(" is%s %s",
7092 (cmdp ? " a tracked alias for" : nullstr), p
7093 );
7094 } else {
7095 out1str(p);
7096 }
7097 break;
7098 }
7099
7100 case CMDFUNCTION:
7101 if (describe_command_verbose) {
7102 out1str(" is a shell function");
7103 } else {
7104 out1str(command);
7105 }
7106 break;
7107
7108 case CMDBUILTIN:
7109 if (describe_command_verbose) {
7110 out1fmt(" is a %sshell builtin",
7111 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7112 "special " : nullstr
7113 );
7114 } else {
7115 out1str(command);
7116 }
7117 break;
7118
7119 default:
7120 if (describe_command_verbose) {
7121 out1str(": not found\n");
7122 }
7123 return 127;
7124 }
7125 out:
7126 outstr("\n", stdout);
7127 return 0;
7128}
7129
7130static int
7131typecmd(int argc, char **argv)
7132{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007133 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007134 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007135 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007136
Denis Vlasenko46846e22007-05-20 13:08:31 +00007137 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007138 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007139 i++;
7140 verbose = 0;
7141 }
7142 while (i < argc) {
7143 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007144 }
7145 return err;
7146}
7147
7148#if ENABLE_ASH_CMDCMD
7149static int
7150commandcmd(int argc, char **argv)
7151{
7152 int c;
7153 enum {
7154 VERIFY_BRIEF = 1,
7155 VERIFY_VERBOSE = 2,
7156 } verify = 0;
7157
7158 while ((c = nextopt("pvV")) != '\0')
7159 if (c == 'V')
7160 verify |= VERIFY_VERBOSE;
7161 else if (c == 'v')
7162 verify |= VERIFY_BRIEF;
7163#if DEBUG
7164 else if (c != 'p')
7165 abort();
7166#endif
7167 if (verify)
7168 return describe_command(*argptr, verify - VERIFY_BRIEF);
7169
7170 return 0;
7171}
7172#endif
7173
7174
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007175/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007176
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007177static int funcblocksize; /* size of structures in function */
7178static int funcstringsize; /* size of strings in node */
7179static void *funcblock; /* block to allocate function from */
7180static char *funcstring; /* block to allocate strings from */
7181
Eric Andersencb57d552001-06-28 07:25:16 +00007182/* flags in argument to evaltree */
Eric Andersenc470f442003-07-28 09:56:35 +00007183#define EV_EXIT 01 /* exit after evaluating tree */
7184#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
7185#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007186
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007187static const short nodesize[26] = {
7188 SHELL_ALIGN(sizeof(struct ncmd)),
7189 SHELL_ALIGN(sizeof(struct npipe)),
7190 SHELL_ALIGN(sizeof(struct nredir)),
7191 SHELL_ALIGN(sizeof(struct nredir)),
7192 SHELL_ALIGN(sizeof(struct nredir)),
7193 SHELL_ALIGN(sizeof(struct nbinary)),
7194 SHELL_ALIGN(sizeof(struct nbinary)),
7195 SHELL_ALIGN(sizeof(struct nbinary)),
7196 SHELL_ALIGN(sizeof(struct nif)),
7197 SHELL_ALIGN(sizeof(struct nbinary)),
7198 SHELL_ALIGN(sizeof(struct nbinary)),
7199 SHELL_ALIGN(sizeof(struct nfor)),
7200 SHELL_ALIGN(sizeof(struct ncase)),
7201 SHELL_ALIGN(sizeof(struct nclist)),
7202 SHELL_ALIGN(sizeof(struct narg)),
7203 SHELL_ALIGN(sizeof(struct narg)),
7204 SHELL_ALIGN(sizeof(struct nfile)),
7205 SHELL_ALIGN(sizeof(struct nfile)),
7206 SHELL_ALIGN(sizeof(struct nfile)),
7207 SHELL_ALIGN(sizeof(struct nfile)),
7208 SHELL_ALIGN(sizeof(struct nfile)),
7209 SHELL_ALIGN(sizeof(struct ndup)),
7210 SHELL_ALIGN(sizeof(struct ndup)),
7211 SHELL_ALIGN(sizeof(struct nhere)),
7212 SHELL_ALIGN(sizeof(struct nhere)),
7213 SHELL_ALIGN(sizeof(struct nnot)),
7214};
7215
7216static void calcsize(union node *n);
7217
7218static void
7219sizenodelist(struct nodelist *lp)
7220{
7221 while (lp) {
7222 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7223 calcsize(lp->n);
7224 lp = lp->next;
7225 }
7226}
7227
7228static void
7229calcsize(union node *n)
7230{
7231 if (n == NULL)
7232 return;
7233 funcblocksize += nodesize[n->type];
7234 switch (n->type) {
7235 case NCMD:
7236 calcsize(n->ncmd.redirect);
7237 calcsize(n->ncmd.args);
7238 calcsize(n->ncmd.assign);
7239 break;
7240 case NPIPE:
7241 sizenodelist(n->npipe.cmdlist);
7242 break;
7243 case NREDIR:
7244 case NBACKGND:
7245 case NSUBSHELL:
7246 calcsize(n->nredir.redirect);
7247 calcsize(n->nredir.n);
7248 break;
7249 case NAND:
7250 case NOR:
7251 case NSEMI:
7252 case NWHILE:
7253 case NUNTIL:
7254 calcsize(n->nbinary.ch2);
7255 calcsize(n->nbinary.ch1);
7256 break;
7257 case NIF:
7258 calcsize(n->nif.elsepart);
7259 calcsize(n->nif.ifpart);
7260 calcsize(n->nif.test);
7261 break;
7262 case NFOR:
7263 funcstringsize += strlen(n->nfor.var) + 1;
7264 calcsize(n->nfor.body);
7265 calcsize(n->nfor.args);
7266 break;
7267 case NCASE:
7268 calcsize(n->ncase.cases);
7269 calcsize(n->ncase.expr);
7270 break;
7271 case NCLIST:
7272 calcsize(n->nclist.body);
7273 calcsize(n->nclist.pattern);
7274 calcsize(n->nclist.next);
7275 break;
7276 case NDEFUN:
7277 case NARG:
7278 sizenodelist(n->narg.backquote);
7279 funcstringsize += strlen(n->narg.text) + 1;
7280 calcsize(n->narg.next);
7281 break;
7282 case NTO:
7283 case NCLOBBER:
7284 case NFROM:
7285 case NFROMTO:
7286 case NAPPEND:
7287 calcsize(n->nfile.fname);
7288 calcsize(n->nfile.next);
7289 break;
7290 case NTOFD:
7291 case NFROMFD:
7292 calcsize(n->ndup.vname);
7293 calcsize(n->ndup.next);
7294 break;
7295 case NHERE:
7296 case NXHERE:
7297 calcsize(n->nhere.doc);
7298 calcsize(n->nhere.next);
7299 break;
7300 case NNOT:
7301 calcsize(n->nnot.com);
7302 break;
7303 };
7304}
7305
7306static char *
7307nodeckstrdup(char *s)
7308{
7309 char *rtn = funcstring;
7310
7311 strcpy(funcstring, s);
7312 funcstring += strlen(s) + 1;
7313 return rtn;
7314}
7315
7316static union node *copynode(union node *);
7317
7318static struct nodelist *
7319copynodelist(struct nodelist *lp)
7320{
7321 struct nodelist *start;
7322 struct nodelist **lpp;
7323
7324 lpp = &start;
7325 while (lp) {
7326 *lpp = funcblock;
7327 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7328 (*lpp)->n = copynode(lp->n);
7329 lp = lp->next;
7330 lpp = &(*lpp)->next;
7331 }
7332 *lpp = NULL;
7333 return start;
7334}
7335
7336static union node *
7337copynode(union node *n)
7338{
7339 union node *new;
7340
7341 if (n == NULL)
7342 return NULL;
7343 new = funcblock;
7344 funcblock = (char *) funcblock + nodesize[n->type];
7345
7346 switch (n->type) {
7347 case NCMD:
7348 new->ncmd.redirect = copynode(n->ncmd.redirect);
7349 new->ncmd.args = copynode(n->ncmd.args);
7350 new->ncmd.assign = copynode(n->ncmd.assign);
7351 break;
7352 case NPIPE:
7353 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
7354 new->npipe.backgnd = n->npipe.backgnd;
7355 break;
7356 case NREDIR:
7357 case NBACKGND:
7358 case NSUBSHELL:
7359 new->nredir.redirect = copynode(n->nredir.redirect);
7360 new->nredir.n = copynode(n->nredir.n);
7361 break;
7362 case NAND:
7363 case NOR:
7364 case NSEMI:
7365 case NWHILE:
7366 case NUNTIL:
7367 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7368 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7369 break;
7370 case NIF:
7371 new->nif.elsepart = copynode(n->nif.elsepart);
7372 new->nif.ifpart = copynode(n->nif.ifpart);
7373 new->nif.test = copynode(n->nif.test);
7374 break;
7375 case NFOR:
7376 new->nfor.var = nodeckstrdup(n->nfor.var);
7377 new->nfor.body = copynode(n->nfor.body);
7378 new->nfor.args = copynode(n->nfor.args);
7379 break;
7380 case NCASE:
7381 new->ncase.cases = copynode(n->ncase.cases);
7382 new->ncase.expr = copynode(n->ncase.expr);
7383 break;
7384 case NCLIST:
7385 new->nclist.body = copynode(n->nclist.body);
7386 new->nclist.pattern = copynode(n->nclist.pattern);
7387 new->nclist.next = copynode(n->nclist.next);
7388 break;
7389 case NDEFUN:
7390 case NARG:
7391 new->narg.backquote = copynodelist(n->narg.backquote);
7392 new->narg.text = nodeckstrdup(n->narg.text);
7393 new->narg.next = copynode(n->narg.next);
7394 break;
7395 case NTO:
7396 case NCLOBBER:
7397 case NFROM:
7398 case NFROMTO:
7399 case NAPPEND:
7400 new->nfile.fname = copynode(n->nfile.fname);
7401 new->nfile.fd = n->nfile.fd;
7402 new->nfile.next = copynode(n->nfile.next);
7403 break;
7404 case NTOFD:
7405 case NFROMFD:
7406 new->ndup.vname = copynode(n->ndup.vname);
7407 new->ndup.dupfd = n->ndup.dupfd;
7408 new->ndup.fd = n->ndup.fd;
7409 new->ndup.next = copynode(n->ndup.next);
7410 break;
7411 case NHERE:
7412 case NXHERE:
7413 new->nhere.doc = copynode(n->nhere.doc);
7414 new->nhere.fd = n->nhere.fd;
7415 new->nhere.next = copynode(n->nhere.next);
7416 break;
7417 case NNOT:
7418 new->nnot.com = copynode(n->nnot.com);
7419 break;
7420 };
7421 new->type = n->type;
7422 return new;
7423}
7424
7425/*
7426 * Make a copy of a parse tree.
7427 */
7428static struct funcnode *
7429copyfunc(union node *n)
7430{
7431 struct funcnode *f;
7432 size_t blocksize;
7433
7434 funcblocksize = offsetof(struct funcnode, n);
7435 funcstringsize = 0;
7436 calcsize(n);
7437 blocksize = funcblocksize;
7438 f = ckmalloc(blocksize + funcstringsize);
7439 funcblock = (char *) f + offsetof(struct funcnode, n);
7440 funcstring = (char *) f + blocksize;
7441 copynode(n);
7442 f->count = 0;
7443 return f;
7444}
7445
7446/*
7447 * Define a shell function.
7448 */
7449static void
7450defun(char *name, union node *func)
7451{
7452 struct cmdentry entry;
7453
7454 INT_OFF;
7455 entry.cmdtype = CMDFUNCTION;
7456 entry.u.func = copyfunc(func);
7457 addcmdentry(name, &entry);
7458 INT_ON;
7459}
7460
7461static int evalskip; /* set if we are skipping commands */
7462/* reasons for skipping commands (see comment on breakcmd routine) */
7463#define SKIPBREAK (1 << 0)
7464#define SKIPCONT (1 << 1)
7465#define SKIPFUNC (1 << 2)
7466#define SKIPFILE (1 << 3)
7467#define SKIPEVAL (1 << 4)
7468static int skipcount; /* number of levels to skip */
7469static int funcnest; /* depth of function calls */
7470
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007471/* forward decl way out to parsing code - dotrap needs it */
7472static int evalstring(char *s, int mask);
7473
7474/*
7475 * Called to execute a trap. Perhaps we should avoid entering new trap
7476 * handlers while we are executing a trap handler.
7477 */
7478static int
7479dotrap(void)
7480{
7481 char *p;
7482 char *q;
7483 int i;
7484 int savestatus;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007485 int skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007486
7487 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007488 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007489 xbarrier();
7490
7491 for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
7492 if (!*q)
7493 continue;
7494 *q = '\0';
7495
7496 p = trap[i + 1];
7497 if (!p)
7498 continue;
7499 skip = evalstring(p, SKIPEVAL);
7500 exitstatus = savestatus;
7501 if (skip)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007502 return skip;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007503 }
7504
Denis Vlasenko991a1da2008-02-10 19:02:53 +00007505 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007506}
7507
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007508/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00007509static void evalloop(union node *, int);
7510static void evalfor(union node *, int);
7511static void evalcase(union node *, int);
7512static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007513static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00007514static void evalpipe(union node *, int);
7515static void evalcommand(union node *, int);
7516static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007517static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00007518
Eric Andersen62483552001-07-10 06:09:16 +00007519/*
Eric Andersenc470f442003-07-28 09:56:35 +00007520 * Evaluate a parse tree. The value is left in the global variable
7521 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00007522 */
Eric Andersenc470f442003-07-28 09:56:35 +00007523static void
7524evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00007525{
Eric Andersenc470f442003-07-28 09:56:35 +00007526 int checkexit = 0;
7527 void (*evalfn)(union node *, int);
7528 unsigned isor;
7529 int status;
7530 if (n == NULL) {
7531 TRACE(("evaltree(NULL) called\n"));
7532 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00007533 }
Eric Andersenc470f442003-07-28 09:56:35 +00007534 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00007535 getpid(), n, n->type, flags));
Eric Andersenc470f442003-07-28 09:56:35 +00007536 switch (n->type) {
7537 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00007538#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00007539 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00007540 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00007541 break;
7542#endif
7543 case NNOT:
7544 evaltree(n->nnot.com, EV_TESTED);
7545 status = !exitstatus;
7546 goto setstatus;
7547 case NREDIR:
7548 expredir(n->nredir.redirect);
7549 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
7550 if (!status) {
7551 evaltree(n->nredir.n, flags & EV_TESTED);
7552 status = exitstatus;
7553 }
7554 popredir(0);
7555 goto setstatus;
7556 case NCMD:
7557 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007558 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00007559 if (eflag && !(flags & EV_TESTED))
7560 checkexit = ~0;
7561 goto calleval;
7562 case NFOR:
7563 evalfn = evalfor;
7564 goto calleval;
7565 case NWHILE:
7566 case NUNTIL:
7567 evalfn = evalloop;
7568 goto calleval;
7569 case NSUBSHELL:
7570 case NBACKGND:
7571 evalfn = evalsubshell;
7572 goto calleval;
7573 case NPIPE:
7574 evalfn = evalpipe;
7575 goto checkexit;
7576 case NCASE:
7577 evalfn = evalcase;
7578 goto calleval;
7579 case NAND:
7580 case NOR:
7581 case NSEMI:
7582#if NAND + 1 != NOR
7583#error NAND + 1 != NOR
7584#endif
7585#if NOR + 1 != NSEMI
7586#error NOR + 1 != NSEMI
7587#endif
7588 isor = n->type - NAND;
7589 evaltree(
7590 n->nbinary.ch1,
7591 (flags | ((isor >> 1) - 1)) & EV_TESTED
7592 );
7593 if (!exitstatus == isor)
7594 break;
7595 if (!evalskip) {
7596 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007597 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00007598 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007599 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00007600 evalfn(n, flags);
7601 break;
7602 }
7603 break;
7604 case NIF:
7605 evaltree(n->nif.test, EV_TESTED);
7606 if (evalskip)
7607 break;
7608 if (exitstatus == 0) {
7609 n = n->nif.ifpart;
7610 goto evaln;
7611 } else if (n->nif.elsepart) {
7612 n = n->nif.elsepart;
7613 goto evaln;
7614 }
7615 goto success;
7616 case NDEFUN:
7617 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007618 success:
Eric Andersenc470f442003-07-28 09:56:35 +00007619 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007620 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00007621 exitstatus = status;
7622 break;
7623 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007624 out:
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007625 if ((checkexit & exitstatus))
7626 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007627 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007628 goto exexit;
7629
7630 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007631 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00007632 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00007633 }
Eric Andersen62483552001-07-10 06:09:16 +00007634}
7635
Eric Andersenc470f442003-07-28 09:56:35 +00007636#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
7637static
7638#endif
7639void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
7640
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00007641static int loopnest; /* current loop nesting level */
Eric Andersenc470f442003-07-28 09:56:35 +00007642
7643static void
7644evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007645{
7646 int status;
7647
7648 loopnest++;
7649 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007650 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00007651 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00007652 int i;
7653
Eric Andersencb57d552001-06-28 07:25:16 +00007654 evaltree(n->nbinary.ch1, EV_TESTED);
7655 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007656 skipping:
7657 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007658 evalskip = 0;
7659 continue;
7660 }
7661 if (evalskip == SKIPBREAK && --skipcount <= 0)
7662 evalskip = 0;
7663 break;
7664 }
Eric Andersenc470f442003-07-28 09:56:35 +00007665 i = exitstatus;
7666 if (n->type != NWHILE)
7667 i = !i;
7668 if (i != 0)
7669 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007670 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007671 status = exitstatus;
7672 if (evalskip)
7673 goto skipping;
7674 }
7675 loopnest--;
7676 exitstatus = status;
7677}
7678
Eric Andersenc470f442003-07-28 09:56:35 +00007679static void
7680evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007681{
7682 struct arglist arglist;
7683 union node *argp;
7684 struct strlist *sp;
7685 struct stackmark smark;
7686
7687 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007688 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007689 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007690 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007691 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00007692 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00007693 if (evalskip)
7694 goto out;
7695 }
7696 *arglist.lastp = NULL;
7697
7698 exitstatus = 0;
7699 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007700 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007701 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007702 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007703 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00007704 if (evalskip) {
7705 if (evalskip == SKIPCONT && --skipcount <= 0) {
7706 evalskip = 0;
7707 continue;
7708 }
7709 if (evalskip == SKIPBREAK && --skipcount <= 0)
7710 evalskip = 0;
7711 break;
7712 }
7713 }
7714 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007715 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007716 popstackmark(&smark);
7717}
7718
Eric Andersenc470f442003-07-28 09:56:35 +00007719static void
7720evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007721{
7722 union node *cp;
7723 union node *patp;
7724 struct arglist arglist;
7725 struct stackmark smark;
7726
7727 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007728 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00007729 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00007730 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00007731 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007732 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
7733 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00007734 if (casematch(patp, arglist.list->text)) {
7735 if (evalskip == 0) {
7736 evaltree(cp->nclist.body, flags);
7737 }
7738 goto out;
7739 }
7740 }
7741 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00007742 out:
Eric Andersencb57d552001-06-28 07:25:16 +00007743 popstackmark(&smark);
7744}
7745
Eric Andersenc470f442003-07-28 09:56:35 +00007746/*
7747 * Kick off a subshell to evaluate a tree.
7748 */
Eric Andersenc470f442003-07-28 09:56:35 +00007749static void
7750evalsubshell(union node *n, int flags)
7751{
7752 struct job *jp;
7753 int backgnd = (n->type == NBACKGND);
7754 int status;
7755
7756 expredir(n->nredir.redirect);
7757 if (!backgnd && flags & EV_EXIT && !trap[0])
7758 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007759 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00007760 jp = makejob(n, 1);
7761 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007762 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007763 flags |= EV_EXIT;
7764 if (backgnd)
7765 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00007766 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00007767 redirect(n->nredir.redirect, 0);
7768 evaltreenr(n->nredir.n, flags);
7769 /* never returns */
7770 }
7771 status = 0;
7772 if (! backgnd)
7773 status = waitforjob(jp);
7774 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007775 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00007776}
7777
Eric Andersenc470f442003-07-28 09:56:35 +00007778/*
7779 * Compute the names of the files in a redirection list.
7780 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00007781static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00007782static void
7783expredir(union node *n)
7784{
7785 union node *redir;
7786
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007787 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00007788 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007789
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00007790 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00007791 fn.lastp = &fn.list;
7792 switch (redir->type) {
7793 case NFROMTO:
7794 case NFROM:
7795 case NTO:
7796 case NCLOBBER:
7797 case NAPPEND:
7798 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
7799 redir->nfile.expfname = fn.list->text;
7800 break;
7801 case NFROMFD:
7802 case NTOFD:
7803 if (redir->ndup.vname) {
7804 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007805 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00007806 ash_msg_and_raise_error("redir error");
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007807 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00007808 }
7809 break;
7810 }
7811 }
7812}
7813
Eric Andersencb57d552001-06-28 07:25:16 +00007814/*
Eric Andersencb57d552001-06-28 07:25:16 +00007815 * Evaluate a pipeline. All the processes in the pipeline are children
7816 * of the process creating the pipeline. (This differs from some versions
7817 * of the shell, which make the last process in a pipeline the parent
7818 * of all the rest.)
7819 */
Eric Andersenc470f442003-07-28 09:56:35 +00007820static void
7821evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00007822{
7823 struct job *jp;
7824 struct nodelist *lp;
7825 int pipelen;
7826 int prevfd;
7827 int pip[2];
7828
Eric Andersenc470f442003-07-28 09:56:35 +00007829 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00007830 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007831 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00007832 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007833 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00007834 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00007835 jp = makejob(n, pipelen);
7836 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00007837 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007838 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00007839 pip[1] = -1;
7840 if (lp->next) {
7841 if (pipe(pip) < 0) {
7842 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00007843 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00007844 }
7845 }
7846 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00007847 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007848 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007849 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00007850 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00007851 if (prevfd > 0) {
7852 dup2(prevfd, 0);
7853 close(prevfd);
7854 }
7855 if (pip[1] > 1) {
7856 dup2(pip[1], 1);
7857 close(pip[1]);
7858 }
Eric Andersenc470f442003-07-28 09:56:35 +00007859 evaltreenr(lp->n, flags);
7860 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00007861 }
7862 if (prevfd >= 0)
7863 close(prevfd);
7864 prevfd = pip[0];
7865 close(pip[1]);
7866 }
Eric Andersencb57d552001-06-28 07:25:16 +00007867 if (n->npipe.backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00007868 exitstatus = waitforjob(jp);
7869 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00007870 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00007871 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00007872}
7873
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007874/*
7875 * Controls whether the shell is interactive or not.
7876 */
7877static void
7878setinteractive(int on)
7879{
7880 static int is_interactive;
7881
7882 if (++on == is_interactive)
7883 return;
7884 is_interactive = on;
7885 setsignal(SIGINT);
7886 setsignal(SIGQUIT);
7887 setsignal(SIGTERM);
7888#if !ENABLE_FEATURE_SH_EXTRA_QUIET
7889 if (is_interactive > 1) {
7890 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007891 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007892
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007893 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007894 out1fmt(
7895 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007896 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007897 "Enter 'help' for a list of built-in commands."
7898 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00007899 bb_banner);
7900 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00007901 }
7902 }
7903#endif
7904}
7905
7906#if ENABLE_FEATURE_EDITING_VI
7907#define setvimode(on) do { \
7908 if (on) line_input_state->flags |= VI_MODE; \
7909 else line_input_state->flags &= ~VI_MODE; \
7910} while (0)
7911#else
7912#define setvimode(on) viflag = 0 /* forcibly keep the option off */
7913#endif
7914
7915static void
7916optschanged(void)
7917{
7918#if DEBUG
7919 opentrace();
7920#endif
7921 setinteractive(iflag);
7922 setjobctl(mflag);
7923 setvimode(viflag);
7924}
7925
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007926static struct localvar *localvars;
7927
7928/*
7929 * Called after a function returns.
7930 * Interrupts must be off.
7931 */
7932static void
7933poplocalvars(void)
7934{
7935 struct localvar *lvp;
7936 struct var *vp;
7937
7938 while ((lvp = localvars) != NULL) {
7939 localvars = lvp->next;
7940 vp = lvp->vp;
7941 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
7942 if (vp == NULL) { /* $- saved */
7943 memcpy(optlist, lvp->text, sizeof(optlist));
7944 free((char*)lvp->text);
7945 optschanged();
7946 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7947 unsetvar(vp->text);
7948 } else {
7949 if (vp->func)
7950 (*vp->func)(strchrnul(lvp->text, '=') + 1);
7951 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
7952 free((char*)vp->text);
7953 vp->flags = lvp->flags;
7954 vp->text = lvp->text;
7955 }
7956 free(lvp);
7957 }
7958}
7959
7960static int
7961evalfun(struct funcnode *func, int argc, char **argv, int flags)
7962{
7963 volatile struct shparam saveparam;
7964 struct localvar *volatile savelocalvars;
7965 struct jmploc *volatile savehandler;
7966 struct jmploc jmploc;
7967 int e;
7968
7969 saveparam = shellparam;
7970 savelocalvars = localvars;
7971 e = setjmp(jmploc.loc);
7972 if (e) {
7973 goto funcdone;
7974 }
7975 INT_OFF;
7976 savehandler = exception_handler;
7977 exception_handler = &jmploc;
7978 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00007979 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007980 func->count++;
7981 funcnest++;
7982 INT_ON;
7983 shellparam.nparam = argc - 1;
7984 shellparam.p = argv + 1;
7985#if ENABLE_ASH_GETOPTS
7986 shellparam.optind = 1;
7987 shellparam.optoff = -1;
7988#endif
7989 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00007990 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007991 INT_OFF;
7992 funcnest--;
7993 freefunc(func);
7994 poplocalvars();
7995 localvars = savelocalvars;
7996 freeparam(&shellparam);
7997 shellparam = saveparam;
7998 exception_handler = savehandler;
7999 INT_ON;
8000 evalskip &= ~SKIPFUNC;
8001 return e;
8002}
8003
Denis Vlasenko131ae172007-02-18 13:00:19 +00008004#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008005static char **
8006parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008007{
8008 char *cp, c;
8009
8010 for (;;) {
8011 cp = *++argv;
8012 if (!cp)
8013 return 0;
8014 if (*cp++ != '-')
8015 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008016 c = *cp++;
8017 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008018 break;
8019 if (c == '-' && !*cp) {
8020 argv++;
8021 break;
8022 }
8023 do {
8024 switch (c) {
8025 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008026 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008027 break;
8028 default:
8029 /* run 'typecmd' for other options */
8030 return 0;
8031 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008032 c = *cp++;
8033 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008034 }
8035 return argv;
8036}
8037#endif
8038
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008039/*
8040 * Make a variable a local variable. When a variable is made local, it's
8041 * value and flags are saved in a localvar structure. The saved values
8042 * will be restored when the shell function returns. We handle the name
8043 * "-" as a special case.
8044 */
8045static void
8046mklocal(char *name)
8047{
8048 struct localvar *lvp;
8049 struct var **vpp;
8050 struct var *vp;
8051
8052 INT_OFF;
8053 lvp = ckmalloc(sizeof(struct localvar));
8054 if (LONE_DASH(name)) {
8055 char *p;
8056 p = ckmalloc(sizeof(optlist));
8057 lvp->text = memcpy(p, optlist, sizeof(optlist));
8058 vp = NULL;
8059 } else {
8060 char *eq;
8061
8062 vpp = hashvar(name);
8063 vp = *findvar(vpp, name);
8064 eq = strchr(name, '=');
8065 if (vp == NULL) {
8066 if (eq)
8067 setvareq(name, VSTRFIXED);
8068 else
8069 setvar(name, NULL, VSTRFIXED);
8070 vp = *vpp; /* the new variable */
8071 lvp->flags = VUNSET;
8072 } else {
8073 lvp->text = vp->text;
8074 lvp->flags = vp->flags;
8075 vp->flags |= VSTRFIXED|VTEXTFIXED;
8076 if (eq)
8077 setvareq(name, 0);
8078 }
8079 }
8080 lvp->vp = vp;
8081 lvp->next = localvars;
8082 localvars = lvp;
8083 INT_ON;
8084}
8085
8086/*
8087 * The "local" command.
8088 */
8089static int
8090localcmd(int argc, char **argv)
8091{
8092 char *name;
8093
8094 argv = argptr;
8095 while ((name = *argv++) != NULL) {
8096 mklocal(name);
8097 }
8098 return 0;
8099}
8100
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008101static int
8102falsecmd(int argc, char **argv)
8103{
8104 return 1;
8105}
8106
8107static int
8108truecmd(int argc, char **argv)
8109{
8110 return 0;
8111}
8112
8113static int
8114execcmd(int argc, char **argv)
8115{
8116 if (argc > 1) {
8117 iflag = 0; /* exit on error */
8118 mflag = 0;
8119 optschanged();
8120 shellexec(argv + 1, pathval(), 0);
8121 }
8122 return 0;
8123}
8124
8125/*
8126 * The return command.
8127 */
8128static int
8129returncmd(int argc, char **argv)
8130{
8131 /*
8132 * If called outside a function, do what ksh does;
8133 * skip the rest of the file.
8134 */
8135 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8136 return argv[1] ? number(argv[1]) : exitstatus;
8137}
8138
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008139/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008140static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008141static int dotcmd(int, char **);
8142static int evalcmd(int, char **);
8143#if ENABLE_ASH_BUILTIN_ECHO
8144static int echocmd(int, char **);
8145#endif
8146#if ENABLE_ASH_BUILTIN_TEST
8147static int testcmd(int, char **);
8148#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008149static int exitcmd(int, char **);
8150static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008151#if ENABLE_ASH_GETOPTS
8152static int getoptscmd(int, char **);
8153#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008154#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8155static int helpcmd(int argc, char **argv);
8156#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008157#if ENABLE_ASH_MATH_SUPPORT
8158static int letcmd(int, char **);
8159#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008160static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008161static int setcmd(int, char **);
8162static int shiftcmd(int, char **);
8163static int timescmd(int, char **);
8164static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008165static int umaskcmd(int, char **);
8166static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008167static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008168
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008169#define BUILTIN_NOSPEC "0"
8170#define BUILTIN_SPECIAL "1"
8171#define BUILTIN_REGULAR "2"
8172#define BUILTIN_SPEC_REG "3"
8173#define BUILTIN_ASSIGN "4"
8174#define BUILTIN_SPEC_ASSG "5"
8175#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008176#define BUILTIN_SPEC_REG_ASSG "7"
8177
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008178/* make sure to keep these in proper order since it is searched via bsearch() */
8179static const struct builtincmd builtintab[] = {
8180 { BUILTIN_SPEC_REG ".", dotcmd },
8181 { BUILTIN_SPEC_REG ":", truecmd },
8182#if ENABLE_ASH_BUILTIN_TEST
8183 { BUILTIN_REGULAR "[", testcmd },
8184 { BUILTIN_REGULAR "[[", testcmd },
8185#endif
8186#if ENABLE_ASH_ALIAS
8187 { BUILTIN_REG_ASSG "alias", aliascmd },
8188#endif
8189#if JOBS
8190 { BUILTIN_REGULAR "bg", fg_bgcmd },
8191#endif
8192 { BUILTIN_SPEC_REG "break", breakcmd },
8193 { BUILTIN_REGULAR "cd", cdcmd },
8194 { BUILTIN_NOSPEC "chdir", cdcmd },
8195#if ENABLE_ASH_CMDCMD
8196 { BUILTIN_REGULAR "command", commandcmd },
8197#endif
8198 { BUILTIN_SPEC_REG "continue", breakcmd },
8199#if ENABLE_ASH_BUILTIN_ECHO
8200 { BUILTIN_REGULAR "echo", echocmd },
8201#endif
8202 { BUILTIN_SPEC_REG "eval", evalcmd },
8203 { BUILTIN_SPEC_REG "exec", execcmd },
8204 { BUILTIN_SPEC_REG "exit", exitcmd },
8205 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8206 { BUILTIN_REGULAR "false", falsecmd },
8207#if JOBS
8208 { BUILTIN_REGULAR "fg", fg_bgcmd },
8209#endif
8210#if ENABLE_ASH_GETOPTS
8211 { BUILTIN_REGULAR "getopts", getoptscmd },
8212#endif
8213 { BUILTIN_NOSPEC "hash", hashcmd },
8214#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8215 { BUILTIN_NOSPEC "help", helpcmd },
8216#endif
8217#if JOBS
8218 { BUILTIN_REGULAR "jobs", jobscmd },
8219 { BUILTIN_REGULAR "kill", killcmd },
8220#endif
8221#if ENABLE_ASH_MATH_SUPPORT
8222 { BUILTIN_NOSPEC "let", letcmd },
8223#endif
8224 { BUILTIN_ASSIGN "local", localcmd },
8225 { BUILTIN_NOSPEC "pwd", pwdcmd },
8226 { BUILTIN_REGULAR "read", readcmd },
8227 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8228 { BUILTIN_SPEC_REG "return", returncmd },
8229 { BUILTIN_SPEC_REG "set", setcmd },
8230 { BUILTIN_SPEC_REG "shift", shiftcmd },
8231 { BUILTIN_SPEC_REG "source", dotcmd },
8232#if ENABLE_ASH_BUILTIN_TEST
8233 { BUILTIN_REGULAR "test", testcmd },
8234#endif
8235 { BUILTIN_SPEC_REG "times", timescmd },
8236 { BUILTIN_SPEC_REG "trap", trapcmd },
8237 { BUILTIN_REGULAR "true", truecmd },
8238 { BUILTIN_NOSPEC "type", typecmd },
8239 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8240 { BUILTIN_REGULAR "umask", umaskcmd },
8241#if ENABLE_ASH_ALIAS
8242 { BUILTIN_REGULAR "unalias", unaliascmd },
8243#endif
8244 { BUILTIN_SPEC_REG "unset", unsetcmd },
8245 { BUILTIN_REGULAR "wait", waitcmd },
8246};
8247
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008248
8249#define COMMANDCMD (builtintab + 5 + \
8250 2 * ENABLE_ASH_BUILTIN_TEST + \
8251 ENABLE_ASH_ALIAS + \
8252 ENABLE_ASH_JOB_CONTROL)
8253#define EXECCMD (builtintab + 7 + \
8254 2 * ENABLE_ASH_BUILTIN_TEST + \
8255 ENABLE_ASH_ALIAS + \
8256 ENABLE_ASH_JOB_CONTROL + \
8257 ENABLE_ASH_CMDCMD + \
8258 ENABLE_ASH_BUILTIN_ECHO)
8259
8260/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008261 * Search the table of builtin commands.
8262 */
8263static struct builtincmd *
8264find_builtin(const char *name)
8265{
8266 struct builtincmd *bp;
8267
8268 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008269 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008270 pstrcmp
8271 );
8272 return bp;
8273}
8274
8275/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008276 * Execute a simple command.
8277 */
8278static int back_exitstatus; /* exit status of backquoted command */
8279static int
8280isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008281{
8282 const char *q = endofname(p);
8283 if (p == q)
8284 return 0;
8285 return *q == '=';
8286}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008287static int
8288bltincmd(int argc, char **argv)
8289{
8290 /* Preserve exitstatus of a previous possible redirection
8291 * as POSIX mandates */
8292 return back_exitstatus;
8293}
Eric Andersenc470f442003-07-28 09:56:35 +00008294static void
8295evalcommand(union node *cmd, int flags)
8296{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008297 static const struct builtincmd null_bltin = {
8298 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008299 };
Eric Andersenc470f442003-07-28 09:56:35 +00008300 struct stackmark smark;
8301 union node *argp;
8302 struct arglist arglist;
8303 struct arglist varlist;
8304 char **argv;
8305 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008306 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008307 struct cmdentry cmdentry;
8308 struct job *jp;
8309 char *lastarg;
8310 const char *path;
8311 int spclbltin;
8312 int cmd_is_exec;
8313 int status;
8314 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008315 struct builtincmd *bcmd;
8316 int pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008317
8318 /* First expand the arguments. */
8319 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8320 setstackmark(&smark);
8321 back_exitstatus = 0;
8322
8323 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008324 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008325 varlist.lastp = &varlist.list;
8326 *varlist.lastp = NULL;
8327 arglist.lastp = &arglist.list;
8328 *arglist.lastp = NULL;
8329
8330 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008331 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008332 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8333 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8334 }
8335
Eric Andersenc470f442003-07-28 09:56:35 +00008336 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8337 struct strlist **spp;
8338
8339 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008340 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008341 expandarg(argp, &arglist, EXP_VARTILDE);
8342 else
8343 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8344
Eric Andersenc470f442003-07-28 09:56:35 +00008345 for (sp = *spp; sp; sp = sp->next)
8346 argc++;
8347 }
8348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008349 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008350 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008351 TRACE(("evalcommand arg: %s\n", sp->text));
8352 *nargv++ = sp->text;
8353 }
8354 *nargv = NULL;
8355
8356 lastarg = NULL;
8357 if (iflag && funcnest == 0 && argc > 0)
8358 lastarg = nargv[-1];
8359
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008360 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008361 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008362 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008363
8364 path = vpath.text;
8365 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8366 struct strlist **spp;
8367 char *p;
8368
8369 spp = varlist.lastp;
8370 expandarg(argp, &varlist, EXP_VARTILDE);
8371
8372 /*
8373 * Modify the command lookup path, if a PATH= assignment
8374 * is present
8375 */
8376 p = (*spp)->text;
8377 if (varequal(p, path))
8378 path = p;
8379 }
8380
8381 /* Print the command if xflag is set. */
8382 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008383 int n;
8384 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008385
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008386 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008387 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008388
8389 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008390 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008391 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008392 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008393 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008394 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008395 p--;
8396 }
8397 }
8398 sp = arglist.list;
8399 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008400 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008401 }
8402
8403 cmd_is_exec = 0;
8404 spclbltin = -1;
8405
8406 /* Now locate the command. */
8407 if (argc) {
8408 const char *oldpath;
8409 int cmd_flag = DO_ERR;
8410
8411 path += 5;
8412 oldpath = path;
8413 for (;;) {
8414 find_command(argv[0], &cmdentry, cmd_flag, path);
8415 if (cmdentry.cmdtype == CMDUNKNOWN) {
8416 status = 127;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008417 flush_stderr();
Eric Andersenc470f442003-07-28 09:56:35 +00008418 goto bail;
8419 }
8420
8421 /* implement bltin and command here */
8422 if (cmdentry.cmdtype != CMDBUILTIN)
8423 break;
8424 if (spclbltin < 0)
8425 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8426 if (cmdentry.u.cmd == EXECCMD)
8427 cmd_is_exec++;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008428#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00008429 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00008430 path = oldpath;
8431 nargv = parse_command_args(argv, &path);
8432 if (!nargv)
8433 break;
8434 argc -= nargv - argv;
8435 argv = nargv;
8436 cmd_flag |= DO_NOFUNC;
8437 } else
8438#endif
8439 break;
8440 }
8441 }
8442
8443 if (status) {
8444 /* We have a redirection error. */
8445 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008446 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008447 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00008448 exitstatus = status;
8449 goto out;
8450 }
8451
8452 /* Execute the command. */
8453 switch (cmdentry.cmdtype) {
8454 default:
8455 /* Fork off a child process if necessary. */
8456 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008457 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +00008458 jp = makejob(cmd, 1);
8459 if (forkshell(jp, cmd, FORK_FG) != 0) {
8460 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008461 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008462 break;
8463 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008464 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008465 }
8466 listsetvar(varlist.list, VEXPORT|VSTACK);
8467 shellexec(argv, path, cmdentry.u.index);
8468 /* NOTREACHED */
8469
8470 case CMDBUILTIN:
8471 cmdenviron = varlist.list;
8472 if (cmdenviron) {
8473 struct strlist *list = cmdenviron;
8474 int i = VNOSET;
8475 if (spclbltin > 0 || argc == 0) {
8476 i = 0;
8477 if (cmd_is_exec && argc > 1)
8478 i = VEXPORT;
8479 }
8480 listsetvar(list, i);
8481 }
8482 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8483 int exit_status;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008484 int i = exception;
Eric Andersenc470f442003-07-28 09:56:35 +00008485 if (i == EXEXIT)
8486 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00008487 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008488 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008489 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00008490 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008491 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00008492 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00008493 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008494 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008495 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008496 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008497 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008498 }
8499 break;
8500
8501 case CMDFUNCTION:
8502 listsetvar(varlist.list, 0);
8503 if (evalfun(cmdentry.u.func, argc, argv, flags))
8504 goto raise;
8505 break;
8506 }
8507
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008508 out:
Eric Andersenc470f442003-07-28 09:56:35 +00008509 popredir(cmd_is_exec);
8510 if (lastarg)
8511 /* dsl: I think this is intended to be used to support
8512 * '_' in 'vi' command mode during line editing...
8513 * However I implemented that within libedit itself.
8514 */
8515 setvar("_", lastarg, 0);
8516 popstackmark(&smark);
8517}
8518
8519static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008520evalbltin(const struct builtincmd *cmd, int argc, char **argv)
8521{
Eric Andersenc470f442003-07-28 09:56:35 +00008522 char *volatile savecmdname;
8523 struct jmploc *volatile savehandler;
8524 struct jmploc jmploc;
8525 int i;
8526
8527 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008528 i = setjmp(jmploc.loc);
8529 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00008530 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008531 savehandler = exception_handler;
8532 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008533 commandname = argv[0];
8534 argptr = argv + 1;
8535 optptr = NULL; /* initialize nextopt */
8536 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008537 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008538 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008539 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00008540 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008541 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008542// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008543 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00008544
8545 return i;
8546}
8547
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008548static int
8549goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008550{
8551 return !*endofname(p);
8552}
8553
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008554
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008555/*
8556 * Search for a command. This is called before we fork so that the
8557 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008558 * the child. The check for "goodname" is an overly conservative
8559 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008560 */
Eric Andersenc470f442003-07-28 09:56:35 +00008561static void
8562prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008563{
8564 struct cmdentry entry;
8565
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008566 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
8567 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008568}
8569
Eric Andersencb57d552001-06-28 07:25:16 +00008570
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008571/* ============ Builtin commands
8572 *
8573 * Builtin commands whose functions are closely tied to evaluation
8574 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00008575 */
8576
8577/*
Eric Andersencb57d552001-06-28 07:25:16 +00008578 * Handle break and continue commands. Break, continue, and return are
8579 * all handled by setting the evalskip flag. The evaluation routines
8580 * above all check this flag, and if it is set they start skipping
8581 * commands rather than executing them. The variable skipcount is
8582 * the number of loops to break/continue, or the number of function
8583 * levels to return. (The latter is always 1.) It should probably
8584 * be an error to break out of more loops than exist, but it isn't
8585 * in the standard shell so we don't make it one here.
8586 */
Eric Andersenc470f442003-07-28 09:56:35 +00008587static int
8588breakcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00008589{
8590 int n = argc > 1 ? number(argv[1]) : 1;
8591
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00008592 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008593 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008594 if (n > loopnest)
8595 n = loopnest;
8596 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008597 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00008598 skipcount = n;
8599 }
8600 return 0;
8601}
8602
Eric Andersenc470f442003-07-28 09:56:35 +00008603
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008604/* ============ input.c
8605 *
Eric Andersen90898442003-08-06 11:20:52 +00008606 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00008607 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008608
Eric Andersenc470f442003-07-28 09:56:35 +00008609#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
Eric Andersencb57d552001-06-28 07:25:16 +00008610
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008611enum {
8612 INPUT_PUSH_FILE = 1,
8613 INPUT_NOFILE_OK = 2,
8614};
Eric Andersencb57d552001-06-28 07:25:16 +00008615
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008616static int plinno = 1; /* input line number */
8617/* number of characters left in input buffer */
8618static int parsenleft; /* copy of parsefile->nleft */
8619static int parselleft; /* copy of parsefile->lleft */
8620/* next character in input buffer */
8621static char *parsenextc; /* copy of parsefile->nextc */
8622
8623static int checkkwd;
8624/* values of checkkwd variable */
8625#define CHKALIAS 0x1
8626#define CHKKWD 0x2
8627#define CHKNL 0x4
8628
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008629static void
8630popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008631{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008632 struct strpush *sp = parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00008633
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008634 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008635#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008636 if (sp->ap) {
8637 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
8638 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008639 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008640 if (sp->string != sp->ap->val) {
8641 free(sp->string);
8642 }
8643 sp->ap->flag &= ~ALIASINUSE;
8644 if (sp->ap->flag & ALIASDEAD) {
8645 unalias(sp->ap->name);
8646 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00008647 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008648#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008649 parsenextc = sp->prevstring;
8650 parsenleft = sp->prevnleft;
8651/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
8652 parsefile->strpush = sp->prev;
8653 if (sp != &(parsefile->basestrpush))
8654 free(sp);
8655 INT_ON;
8656}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008657
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008658static int
8659preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008660{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008661 int nr;
Eric Andersenc470f442003-07-28 09:56:35 +00008662 char *buf = parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008663 parsenextc = buf;
8664
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008665 retry:
Denis Vlasenko38f63192007-01-22 09:03:07 +00008666#if ENABLE_FEATURE_EDITING
Eric Andersenc470f442003-07-28 09:56:35 +00008667 if (!iflag || parsefile->fd)
8668 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
8669 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00008670#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008671 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00008672#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008673 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
8674 if (nr == 0) {
8675 /* Ctrl+C pressed */
8676 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008677 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008678 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00008679 raise(SIGINT);
8680 return 1;
8681 }
Eric Andersenc470f442003-07-28 09:56:35 +00008682 goto retry;
8683 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00008684 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008685 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00008686 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008687 }
Eric Andersencb57d552001-06-28 07:25:16 +00008688 }
8689#else
Eric Andersen7467c8d2001-07-12 20:26:32 +00008690 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00008691#endif
8692
8693 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008694 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00008695 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00008696 if (flags >= 0 && (flags & O_NONBLOCK)) {
8697 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00008698 if (fcntl(0, F_SETFL, flags) >= 0) {
8699 out2str("sh: turning off NDELAY mode\n");
8700 goto retry;
8701 }
8702 }
8703 }
8704 }
8705 return nr;
8706}
8707
8708/*
8709 * Refill the input buffer and return the next input character:
8710 *
8711 * 1) If a string was pushed back on the input, pop it;
8712 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
8713 * from a string so we can't refill the buffer, return EOF.
8714 * 3) If the is more stuff in this buffer, use it else call read to fill it.
8715 * 4) Process input up to the next newline, deleting nul characters.
8716 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008717static int
Eric Andersenc470f442003-07-28 09:56:35 +00008718preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00008719{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008720 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00008721 int more;
8722 char savec;
8723
8724 while (parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00008725#if ENABLE_ASH_ALIAS
Eric Andersen2870d962001-07-02 17:27:21 +00008726 if (parsenleft == -1 && parsefile->strpush->ap &&
8727 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +00008728 return PEOA;
8729 }
Eric Andersen2870d962001-07-02 17:27:21 +00008730#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008731 popstring();
8732 if (--parsenleft >= 0)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008733 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008734 }
8735 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
8736 return PEOF;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008737 flush_stdout_stderr();
Eric Andersencb57d552001-06-28 07:25:16 +00008738
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008739 more = parselleft;
8740 if (more <= 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008741 again:
8742 more = preadfd();
8743 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008744 parselleft = parsenleft = EOF_NLEFT;
8745 return PEOF;
8746 }
8747 }
8748
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008749 q = parsenextc;
Eric Andersencb57d552001-06-28 07:25:16 +00008750
8751 /* delete nul characters */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008752 for (;;) {
8753 int c;
Eric Andersencb57d552001-06-28 07:25:16 +00008754
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008755 more--;
8756 c = *q;
Eric Andersenc470f442003-07-28 09:56:35 +00008757
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008758 if (!c)
8759 memmove(q, q + 1, more);
8760 else {
8761 q++;
8762 if (c == '\n') {
8763 parsenleft = q - parsenextc - 1;
8764 break;
8765 }
Eric Andersencb57d552001-06-28 07:25:16 +00008766 }
8767
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008768 if (more <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008769 parsenleft = q - parsenextc - 1;
8770 if (parsenleft < 0)
8771 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008772 break;
Eric Andersencb57d552001-06-28 07:25:16 +00008773 }
8774 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008775 parselleft = more;
Eric Andersencb57d552001-06-28 07:25:16 +00008776
8777 savec = *q;
8778 *q = '\0';
8779
8780 if (vflag) {
8781 out2str(parsenextc);
Eric Andersencb57d552001-06-28 07:25:16 +00008782 }
8783
8784 *q = savec;
8785
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008786 return signed_char2int(*parsenextc++);
Eric Andersencb57d552001-06-28 07:25:16 +00008787}
8788
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008789#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008790static int
8791pgetc(void)
8792{
8793 return pgetc_as_macro();
8794}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008795
8796#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
8797#define pgetc_macro() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008798#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00008799#define pgetc_macro() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008800#endif
8801
8802/*
8803 * Same as pgetc(), but ignores PEOA.
8804 */
8805#if ENABLE_ASH_ALIAS
8806static int
8807pgetc2(void)
8808{
8809 int c;
8810
8811 do {
8812 c = pgetc_macro();
8813 } while (c == PEOA);
8814 return c;
8815}
8816#else
8817static int
8818pgetc2(void)
8819{
8820 return pgetc_macro();
8821}
8822#endif
8823
8824/*
8825 * Read a line from the script.
8826 */
8827static char *
8828pfgets(char *line, int len)
8829{
8830 char *p = line;
8831 int nleft = len;
8832 int c;
8833
8834 while (--nleft > 0) {
8835 c = pgetc2();
8836 if (c == PEOF) {
8837 if (p == line)
8838 return NULL;
8839 break;
8840 }
8841 *p++ = c;
8842 if (c == '\n')
8843 break;
8844 }
8845 *p = '\0';
8846 return line;
8847}
8848
Eric Andersenc470f442003-07-28 09:56:35 +00008849/*
8850 * Undo the last call to pgetc. Only one character may be pushed back.
8851 * PEOF may be pushed back.
8852 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008853static void
Eric Andersenc470f442003-07-28 09:56:35 +00008854pungetc(void)
8855{
8856 parsenleft++;
8857 parsenextc--;
8858}
Eric Andersencb57d552001-06-28 07:25:16 +00008859
8860/*
8861 * Push a string back onto the input at this current parsefile level.
8862 * We handle aliases this way.
8863 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008864static void
Eric Andersenc470f442003-07-28 09:56:35 +00008865pushstring(char *s, void *ap)
Eric Andersen2870d962001-07-02 17:27:21 +00008866{
Eric Andersencb57d552001-06-28 07:25:16 +00008867 struct strpush *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008868 size_t len;
Eric Andersencb57d552001-06-28 07:25:16 +00008869
Eric Andersenc470f442003-07-28 09:56:35 +00008870 len = strlen(s);
Denis Vlasenkob012b102007-02-19 22:43:01 +00008871 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00008872/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
8873 if (parsefile->strpush) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008874 sp = ckmalloc(sizeof(struct strpush));
Eric Andersencb57d552001-06-28 07:25:16 +00008875 sp->prev = parsefile->strpush;
8876 parsefile->strpush = sp;
8877 } else
8878 sp = parsefile->strpush = &(parsefile->basestrpush);
8879 sp->prevstring = parsenextc;
8880 sp->prevnleft = parsenleft;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008881#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00008882 sp->ap = (struct alias *)ap;
Eric Andersencb57d552001-06-28 07:25:16 +00008883 if (ap) {
Eric Andersenc470f442003-07-28 09:56:35 +00008884 ((struct alias *)ap)->flag |= ALIASINUSE;
Eric Andersencb57d552001-06-28 07:25:16 +00008885 sp->string = s;
8886 }
Eric Andersen2870d962001-07-02 17:27:21 +00008887#endif
Eric Andersencb57d552001-06-28 07:25:16 +00008888 parsenextc = s;
8889 parsenleft = len;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008890 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008891}
8892
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008893/*
8894 * To handle the "." command, a stack of input files is used. Pushfile
8895 * adds a new entry to the stack and popfile restores the previous level.
8896 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008897static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008898pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00008899{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008900 struct parsefile *pf;
8901
8902 parsefile->nleft = parsenleft;
8903 parsefile->lleft = parselleft;
8904 parsefile->nextc = parsenextc;
8905 parsefile->linno = plinno;
8906 pf = ckmalloc(sizeof(*pf));
8907 pf->prev = parsefile;
8908 pf->fd = -1;
8909 pf->strpush = NULL;
8910 pf->basestrpush.prev = NULL;
8911 parsefile = pf;
8912}
8913
8914static void
8915popfile(void)
8916{
8917 struct parsefile *pf = parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00008918
Denis Vlasenkob012b102007-02-19 22:43:01 +00008919 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008920 if (pf->fd >= 0)
8921 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00008922 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008923 while (pf->strpush)
8924 popstring();
8925 parsefile = pf->prev;
8926 free(pf);
8927 parsenleft = parsefile->nleft;
8928 parselleft = parsefile->lleft;
8929 parsenextc = parsefile->nextc;
8930 plinno = parsefile->linno;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008931 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008932}
8933
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008934/*
8935 * Return to top level.
8936 */
8937static void
8938popallfiles(void)
8939{
8940 while (parsefile != &basepf)
8941 popfile();
8942}
8943
8944/*
8945 * Close the file(s) that the shell is reading commands from. Called
8946 * after a fork is done.
8947 */
8948static void
8949closescript(void)
8950{
8951 popallfiles();
8952 if (parsefile->fd > 0) {
8953 close(parsefile->fd);
8954 parsefile->fd = 0;
8955 }
8956}
8957
8958/*
8959 * Like setinputfile, but takes an open file descriptor. Call this with
8960 * interrupts off.
8961 */
8962static void
8963setinputfd(int fd, int push)
8964{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00008965 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008966 if (push) {
8967 pushfile();
8968 parsefile->buf = 0;
8969 }
8970 parsefile->fd = fd;
8971 if (parsefile->buf == NULL)
8972 parsefile->buf = ckmalloc(IBUFSIZ);
8973 parselleft = parsenleft = 0;
8974 plinno = 1;
8975}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008976
Eric Andersenc470f442003-07-28 09:56:35 +00008977/*
8978 * Set the input to take input from a file. If push is set, push the
8979 * old input onto the stack first.
8980 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008981static int
8982setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00008983{
8984 int fd;
8985 int fd2;
8986
Denis Vlasenkob012b102007-02-19 22:43:01 +00008987 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008988 fd = open(fname, O_RDONLY);
8989 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008990 if (flags & INPUT_NOFILE_OK)
8991 goto out;
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008992 ash_msg_and_raise_error("can't open %s", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008993 }
Eric Andersenc470f442003-07-28 09:56:35 +00008994 if (fd < 10) {
8995 fd2 = copyfd(fd, 10);
8996 close(fd);
8997 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008998 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00008999 fd = fd2;
9000 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009001 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009002 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009003 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009004 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009005}
9006
Eric Andersencb57d552001-06-28 07:25:16 +00009007/*
9008 * Like setinputfile, but takes input from a string.
9009 */
Eric Andersenc470f442003-07-28 09:56:35 +00009010static void
9011setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009012{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009013 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009014 pushfile();
9015 parsenextc = string;
9016 parsenleft = strlen(string);
9017 parsefile->buf = NULL;
9018 plinno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009019 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009020}
9021
9022
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009023/* ============ mail.c
9024 *
9025 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009026 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009027
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009028#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009029
Eric Andersencb57d552001-06-28 07:25:16 +00009030#define MAXMBOXES 10
9031
Eric Andersenc470f442003-07-28 09:56:35 +00009032/* times of mailboxes */
9033static time_t mailtime[MAXMBOXES];
9034/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009035static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009036
Eric Andersencb57d552001-06-28 07:25:16 +00009037/*
Eric Andersenc470f442003-07-28 09:56:35 +00009038 * Print appropriate message(s) if mail has arrived.
9039 * If mail_var_path_changed is set,
9040 * then the value of MAIL has mail_var_path_changed,
9041 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009042 */
Eric Andersenc470f442003-07-28 09:56:35 +00009043static void
9044chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009045{
Eric Andersencb57d552001-06-28 07:25:16 +00009046 const char *mpath;
9047 char *p;
9048 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009049 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009050 struct stackmark smark;
9051 struct stat statb;
9052
Eric Andersencb57d552001-06-28 07:25:16 +00009053 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009054 mpath = mpathset() ? mpathval() : mailval();
9055 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009056 p = padvance(&mpath, nullstr);
9057 if (p == NULL)
9058 break;
9059 if (*p == '\0')
9060 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009061 for (q = p; *q; q++);
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009062#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009063 if (q[-1] != '/')
9064 abort();
9065#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009066 q[-1] = '\0'; /* delete trailing '/' */
9067 if (stat(p, &statb) < 0) {
9068 *mtp = 0;
9069 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009070 }
Eric Andersenc470f442003-07-28 09:56:35 +00009071 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9072 fprintf(
9073 stderr, snlfmt,
9074 pathopt ? pathopt : "you have mail"
9075 );
9076 }
9077 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009078 }
Eric Andersenc470f442003-07-28 09:56:35 +00009079 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009080 popstackmark(&smark);
9081}
Eric Andersencb57d552001-06-28 07:25:16 +00009082
Eric Andersenc470f442003-07-28 09:56:35 +00009083static void
9084changemail(const char *val)
9085{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009086 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009087}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009088
Denis Vlasenko131ae172007-02-18 13:00:19 +00009089#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009090
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009091
9092/* ============ ??? */
9093
Eric Andersencb57d552001-06-28 07:25:16 +00009094/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009095 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009096 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009097static void
9098setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009099{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009100 char **newparam;
9101 char **ap;
9102 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009103
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009104 for (nparam = 0; argv[nparam]; nparam++);
9105 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9106 while (*argv) {
9107 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009108 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009109 *ap = NULL;
9110 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009111 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009112 shellparam.nparam = nparam;
9113 shellparam.p = newparam;
9114#if ENABLE_ASH_GETOPTS
9115 shellparam.optind = 1;
9116 shellparam.optoff = -1;
9117#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009118}
9119
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009120/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009121 * Process shell options. The global variable argptr contains a pointer
9122 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009123 *
9124 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9125 * For a non-interactive shell, an error condition encountered
9126 * by a special built-in ... shall cause the shell to write a diagnostic message
9127 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009128 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009129 * ...
9130 * Utility syntax error (option or operand error) Shall exit
9131 * ...
9132 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9133 * we see that bash does not do that (set "finishes" with error code 1 instead,
9134 * and shell continues), and people rely on this behavior!
9135 * Testcase:
9136 * set -o barfoo 2>/dev/null
9137 * echo $?
9138 *
9139 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009140 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009141static int
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009142minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009143{
9144 int i;
9145
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009146 if (name) {
9147 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009148 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009149 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009150 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009151 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009152 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009153 ash_msg("illegal option -o %s", name);
9154 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009155 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009156 out1str("Current option settings\n");
9157 for (i = 0; i < NOPTS; i++)
9158 out1fmt("%-16s%s\n", optnames(i),
9159 optlist[i] ? "on" : "off");
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009160 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009161}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009162static void
9163setoption(int flag, int val)
9164{
9165 int i;
9166
9167 for (i = 0; i < NOPTS; i++) {
9168 if (optletters(i) == flag) {
9169 optlist[i] = val;
9170 return;
9171 }
9172 }
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009173 ash_msg_and_raise_error("illegal option -%c", flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009174 /* NOTREACHED */
9175}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009176static int
Eric Andersenc470f442003-07-28 09:56:35 +00009177options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009178{
9179 char *p;
9180 int val;
9181 int c;
9182
9183 if (cmdline)
9184 minusc = NULL;
9185 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009186 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009187 if (c != '-' && c != '+')
9188 break;
9189 argptr++;
9190 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009191 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009192 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009193 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009194 if (!cmdline) {
9195 /* "-" means turn off -x and -v */
9196 if (p[0] == '\0')
9197 xflag = vflag = 0;
9198 /* "--" means reset params */
9199 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009200 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009201 }
Eric Andersenc470f442003-07-28 09:56:35 +00009202 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009203 }
Eric Andersencb57d552001-06-28 07:25:16 +00009204 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009205 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009206 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009207 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009208 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009209 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009210 } else if (c == 'o') {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009211 if (minus_o(*argptr, val)) {
9212 /* it already printed err message */
9213 return 1; /* error */
9214 }
Eric Andersencb57d552001-06-28 07:25:16 +00009215 if (*argptr)
9216 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009217 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9218 isloginsh = 1;
9219 /* bash does not accept +-login, we also won't */
9220 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009221 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009222 isloginsh = 1;
9223 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009224 } else {
9225 setoption(c, val);
9226 }
9227 }
9228 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009229 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009230}
9231
Eric Andersencb57d552001-06-28 07:25:16 +00009232/*
Eric Andersencb57d552001-06-28 07:25:16 +00009233 * The shift builtin command.
9234 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009235static int
Eric Andersenc470f442003-07-28 09:56:35 +00009236shiftcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009237{
9238 int n;
9239 char **ap1, **ap2;
9240
9241 n = 1;
9242 if (argc > 1)
9243 n = number(argv[1]);
9244 if (n > shellparam.nparam)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009245 ash_msg_and_raise_error("can't shift that many");
9246 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009247 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009248 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009249 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009250 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009251 }
9252 ap2 = shellparam.p;
9253 while ((*ap2++ = *ap1++) != NULL);
Denis Vlasenko131ae172007-02-18 13:00:19 +00009254#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009255 shellparam.optind = 1;
9256 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009257#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009258 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009259 return 0;
9260}
9261
Eric Andersencb57d552001-06-28 07:25:16 +00009262/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009263 * POSIX requires that 'set' (but not export or readonly) output the
9264 * variables in lexicographic order - by the locale's collating order (sigh).
9265 * Maybe we could keep them in an ordered balanced binary tree
9266 * instead of hashed lists.
9267 * For now just roll 'em through qsort for printing...
9268 */
9269static int
9270showvars(const char *sep_prefix, int on, int off)
9271{
9272 const char *sep;
9273 char **ep, **epend;
9274
9275 ep = listvars(on, off, &epend);
9276 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9277
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009278 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009279
9280 for (; ep < epend; ep++) {
9281 const char *p;
9282 const char *q;
9283
9284 p = strchrnul(*ep, '=');
9285 q = nullstr;
9286 if (*p)
9287 q = single_quote(++p);
9288 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9289 }
9290 return 0;
9291}
9292
9293/*
Eric Andersencb57d552001-06-28 07:25:16 +00009294 * The set command builtin.
9295 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009296static int
Eric Andersenc470f442003-07-28 09:56:35 +00009297setcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009298{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009299 int retval;
9300
Eric Andersencb57d552001-06-28 07:25:16 +00009301 if (argc == 1)
Eric Andersenc470f442003-07-28 09:56:35 +00009302 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009303 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009304 retval = 1;
9305 if (!options(0)) { /* if no parse error... */
9306 retval = 0;
9307 optschanged();
9308 if (*argptr != NULL) {
9309 setparam(argptr);
9310 }
Eric Andersencb57d552001-06-28 07:25:16 +00009311 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009312 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009313 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009314}
9315
Denis Vlasenko131ae172007-02-18 13:00:19 +00009316#if ENABLE_ASH_RANDOM_SUPPORT
Eric Andersenef02f822004-03-11 13:34:24 +00009317/* Roughly copied from bash.. */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009318static void
9319change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009320{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009321 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009322 /* "get", generate */
9323 char buf[16];
9324
9325 rseed = rseed * 1103515245 + 12345;
9326 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9327 /* set without recursion */
9328 setvar(vrandom.text, buf, VNOFUNC);
9329 vrandom.flags &= ~VNOFUNC;
9330 } else {
9331 /* set/reset */
9332 rseed = strtoul(value, (char **)NULL, 10);
9333 }
Eric Andersenef02f822004-03-11 13:34:24 +00009334}
Eric Andersen16767e22004-03-16 05:14:10 +00009335#endif
9336
Denis Vlasenko131ae172007-02-18 13:00:19 +00009337#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009338static int
Eric Andersenc470f442003-07-28 09:56:35 +00009339getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009340{
9341 char *p, *q;
9342 char c = '?';
9343 int done = 0;
9344 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +00009345 char s[12];
9346 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +00009347
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009348 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +00009349 return 1;
9350 optnext = optfirst + *param_optind - 1;
9351
9352 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +00009353 p = NULL;
9354 else
Eric Andersena48b0a32003-10-22 10:56:47 +00009355 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +00009356 if (p == NULL || *p == '\0') {
9357 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +00009358 p = *optnext;
9359 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009360 atend:
Eric Andersencb57d552001-06-28 07:25:16 +00009361 p = NULL;
9362 done = 1;
9363 goto out;
9364 }
9365 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009366 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +00009367 goto atend;
9368 }
9369
9370 c = *p++;
Eric Andersenc470f442003-07-28 09:56:35 +00009371 for (q = optstr; *q != c; ) {
Eric Andersencb57d552001-06-28 07:25:16 +00009372 if (*q == '\0') {
9373 if (optstr[0] == ':') {
9374 s[0] = c;
9375 s[1] = '\0';
9376 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009377 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009378 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009379 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009380 }
9381 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +00009382 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009383 }
9384 if (*++q == ':')
9385 q++;
9386 }
9387
9388 if (*++q == ':') {
9389 if (*p == '\0' && (p = *optnext) == NULL) {
9390 if (optstr[0] == ':') {
9391 s[0] = c;
9392 s[1] = '\0';
9393 err |= setvarsafe("OPTARG", s, 0);
9394 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009395 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009396 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009397 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +00009398 c = '?';
9399 }
Eric Andersenc470f442003-07-28 09:56:35 +00009400 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +00009401 }
9402
9403 if (p == *optnext)
9404 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +00009405 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +00009406 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009407 } else
Eric Andersenc470f442003-07-28 09:56:35 +00009408 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009409 out:
Eric Andersencb57d552001-06-28 07:25:16 +00009410 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009411 *param_optind = optnext - optfirst + 1;
9412 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +00009413 err |= setvarsafe("OPTIND", s, VNOFUNC);
9414 s[0] = c;
9415 s[1] = '\0';
9416 err |= setvarsafe(optvar, s, 0);
9417 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +00009418 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009419 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009420 flush_stdout_stderr();
9421 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +00009422 }
9423 return done;
9424}
Eric Andersenc470f442003-07-28 09:56:35 +00009425
9426/*
9427 * The getopts builtin. Shellparam.optnext points to the next argument
9428 * to be processed. Shellparam.optptr points to the next character to
9429 * be processed in the current argument. If shellparam.optnext is NULL,
9430 * then it's the first time getopts has been called.
9431 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009432static int
Eric Andersenc470f442003-07-28 09:56:35 +00009433getoptscmd(int argc, char **argv)
9434{
9435 char **optbase;
9436
9437 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009438 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009439 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +00009440 optbase = shellparam.p;
9441 if (shellparam.optind > shellparam.nparam + 1) {
9442 shellparam.optind = 1;
9443 shellparam.optoff = -1;
9444 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009445 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009446 optbase = &argv[3];
9447 if (shellparam.optind > argc - 2) {
9448 shellparam.optind = 1;
9449 shellparam.optoff = -1;
9450 }
9451 }
9452
9453 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009454 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +00009455}
Denis Vlasenko131ae172007-02-18 13:00:19 +00009456#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +00009457
Eric Andersencb57d552001-06-28 07:25:16 +00009458
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009459/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +00009460
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009461/*
9462 * NEOF is returned by parsecmd when it encounters an end of file. It
9463 * must be distinct from NULL, so we use the address of a variable that
9464 * happens to be handy.
9465 */
9466static smallint tokpushback; /* last token pushed back */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009467#define NEOF ((union node *)&tokpushback)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009468static smallint parsebackquote; /* nonzero if we are inside backquotes */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009469static int lasttoken; /* last token read */
9470static char *wordtext; /* text of last word returned by readtoken */
9471static struct nodelist *backquotelist;
9472static union node *redirnode;
9473static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009474static smallint quoteflag; /* set if (part of) last token was quoted */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009475
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009476static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
9477static void
9478raise_error_syntax(const char *msg)
9479{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009480 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009481 /* NOTREACHED */
9482}
9483
9484/*
9485 * Called when an unexpected token is read during the parse. The argument
9486 * is the token that is expected, or -1 if more than one type of token can
9487 * occur at this point.
9488 */
9489static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
9490static void
9491raise_error_unexpected_syntax(int token)
9492{
9493 char msg[64];
9494 int l;
9495
9496 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
9497 if (token >= 0)
9498 sprintf(msg + l, " (expecting %s)", tokname(token));
9499 raise_error_syntax(msg);
9500 /* NOTREACHED */
9501}
Eric Andersencb57d552001-06-28 07:25:16 +00009502
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009503#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +00009504
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009505struct heredoc {
9506 struct heredoc *next; /* next here document in list */
9507 union node *here; /* redirection node */
9508 char *eofmark; /* string indicating end of input */
9509 int striptabs; /* if set, strip leading tabs */
9510};
Eric Andersencb57d552001-06-28 07:25:16 +00009511
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009512static struct heredoc *heredoclist; /* list of here documents to read */
9513
9514/* parsing is heavily cross-recursive, need these forward decls */
9515static union node *andor(void);
9516static union node *pipeline(void);
9517static union node *parse_command(void);
9518static void parseheredoc(void);
9519static char peektoken(void);
9520static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +00009521
Eric Andersenc470f442003-07-28 09:56:35 +00009522static union node *
9523list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +00009524{
9525 union node *n1, *n2, *n3;
9526 int tok;
9527
Eric Andersenc470f442003-07-28 09:56:35 +00009528 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9529 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009530 return NULL;
9531 n1 = NULL;
9532 for (;;) {
9533 n2 = andor();
9534 tok = readtoken();
9535 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +00009536 if (n2->type == NPIPE) {
9537 n2->npipe.backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009538 } else {
Eric Andersenc470f442003-07-28 09:56:35 +00009539 if (n2->type != NREDIR) {
9540 n3 = stalloc(sizeof(struct nredir));
9541 n3->nredir.n = n2;
9542 n3->nredir.redirect = NULL;
9543 n2 = n3;
9544 }
9545 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +00009546 }
9547 }
9548 if (n1 == NULL) {
9549 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009550 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009551 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009552 n3->type = NSEMI;
9553 n3->nbinary.ch1 = n1;
9554 n3->nbinary.ch2 = n2;
9555 n1 = n3;
9556 }
9557 switch (tok) {
9558 case TBACKGND:
9559 case TSEMI:
9560 tok = readtoken();
9561 /* fall through */
9562 case TNL:
9563 if (tok == TNL) {
9564 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +00009565 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +00009566 return n1;
9567 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009568 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009569 }
Eric Andersenc470f442003-07-28 09:56:35 +00009570 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00009571 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +00009572 return n1;
9573 break;
9574 case TEOF:
9575 if (heredoclist)
9576 parseheredoc();
9577 else
Eric Andersenc470f442003-07-28 09:56:35 +00009578 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +00009579 return n1;
9580 default:
Eric Andersenc470f442003-07-28 09:56:35 +00009581 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009582 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009583 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009584 return n1;
9585 }
9586 }
9587}
9588
Eric Andersenc470f442003-07-28 09:56:35 +00009589static union node *
9590andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009591{
Eric Andersencb57d552001-06-28 07:25:16 +00009592 union node *n1, *n2, *n3;
9593 int t;
9594
Eric Andersencb57d552001-06-28 07:25:16 +00009595 n1 = pipeline();
9596 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009597 t = readtoken();
9598 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +00009599 t = NAND;
9600 } else if (t == TOR) {
9601 t = NOR;
9602 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009603 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009604 return n1;
9605 }
Eric Andersenc470f442003-07-28 09:56:35 +00009606 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009607 n2 = pipeline();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009608 n3 = stalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +00009609 n3->type = t;
9610 n3->nbinary.ch1 = n1;
9611 n3->nbinary.ch2 = n2;
9612 n1 = n3;
9613 }
9614}
9615
Eric Andersenc470f442003-07-28 09:56:35 +00009616static union node *
9617pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009618{
Eric Andersencb57d552001-06-28 07:25:16 +00009619 union node *n1, *n2, *pipenode;
9620 struct nodelist *lp, *prev;
9621 int negate;
9622
9623 negate = 0;
9624 TRACE(("pipeline: entered\n"));
9625 if (readtoken() == TNOT) {
9626 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +00009627 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009628 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009629 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009630 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009631 if (readtoken() == TPIPE) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009632 pipenode = stalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +00009633 pipenode->type = NPIPE;
9634 pipenode->npipe.backgnd = 0;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009635 lp = stalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +00009636 pipenode->npipe.cmdlist = lp;
9637 lp->n = n1;
9638 do {
9639 prev = lp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009640 lp = stalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +00009641 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009642 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +00009643 prev->next = lp;
9644 } while (readtoken() == TPIPE);
9645 lp->next = NULL;
9646 n1 = pipenode;
9647 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009648 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009649 if (negate) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009650 n2 = stalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +00009651 n2->type = NNOT;
9652 n2->nnot.com = n1;
9653 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009654 }
9655 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +00009656}
9657
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009658static union node *
9659makename(void)
9660{
9661 union node *n;
9662
9663 n = stalloc(sizeof(struct narg));
9664 n->type = NARG;
9665 n->narg.next = NULL;
9666 n->narg.text = wordtext;
9667 n->narg.backquote = backquotelist;
9668 return n;
9669}
9670
9671static void
9672fixredir(union node *n, const char *text, int err)
9673{
9674 TRACE(("Fix redir %s %d\n", text, err));
9675 if (!err)
9676 n->ndup.vname = NULL;
9677
9678 if (isdigit(text[0]) && text[1] == '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009679 n->ndup.dupfd = text[0] - '0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009680 else if (LONE_DASH(text))
9681 n->ndup.dupfd = -1;
9682 else {
9683 if (err)
9684 raise_error_syntax("Bad fd number");
9685 n->ndup.vname = makename();
9686 }
9687}
9688
9689/*
9690 * Returns true if the text contains nothing to expand (no dollar signs
9691 * or backquotes).
9692 */
9693static int
9694noexpand(char *text)
9695{
9696 char *p;
9697 char c;
9698
9699 p = text;
9700 while ((c = *p++) != '\0') {
9701 if (c == CTLQUOTEMARK)
9702 continue;
9703 if (c == CTLESC)
9704 p++;
9705 else if (SIT(c, BASESYNTAX) == CCTL)
9706 return 0;
9707 }
9708 return 1;
9709}
9710
9711static void
9712parsefname(void)
9713{
9714 union node *n = redirnode;
9715
9716 if (readtoken() != TWORD)
9717 raise_error_unexpected_syntax(-1);
9718 if (n->type == NHERE) {
9719 struct heredoc *here = heredoc;
9720 struct heredoc *p;
9721 int i;
9722
9723 if (quoteflag == 0)
9724 n->type = NXHERE;
9725 TRACE(("Here document %d\n", n->type));
9726 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9727 raise_error_syntax("Illegal eof marker for << redirection");
9728 rmescapes(wordtext);
9729 here->eofmark = wordtext;
9730 here->next = NULL;
9731 if (heredoclist == NULL)
9732 heredoclist = here;
9733 else {
9734 for (p = heredoclist; p->next; p = p->next);
9735 p->next = here;
9736 }
9737 } else if (n->type == NTOFD || n->type == NFROMFD) {
9738 fixredir(n, wordtext, 0);
9739 } else {
9740 n->nfile.fname = makename();
9741 }
9742}
Eric Andersencb57d552001-06-28 07:25:16 +00009743
Eric Andersenc470f442003-07-28 09:56:35 +00009744static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009745simplecmd(void)
9746{
9747 union node *args, **app;
9748 union node *n = NULL;
9749 union node *vars, **vpp;
9750 union node **rpp, *redir;
9751 int savecheckkwd;
9752
9753 args = NULL;
9754 app = &args;
9755 vars = NULL;
9756 vpp = &vars;
9757 redir = NULL;
9758 rpp = &redir;
9759
9760 savecheckkwd = CHKALIAS;
9761 for (;;) {
9762 checkkwd = savecheckkwd;
9763 switch (readtoken()) {
9764 case TWORD:
9765 n = stalloc(sizeof(struct narg));
9766 n->type = NARG;
9767 n->narg.text = wordtext;
9768 n->narg.backquote = backquotelist;
9769 if (savecheckkwd && isassignment(wordtext)) {
9770 *vpp = n;
9771 vpp = &n->narg.next;
9772 } else {
9773 *app = n;
9774 app = &n->narg.next;
9775 savecheckkwd = 0;
9776 }
9777 break;
9778 case TREDIR:
9779 *rpp = n = redirnode;
9780 rpp = &n->nfile.next;
9781 parsefname(); /* read name of redirection file */
9782 break;
9783 case TLP:
9784 if (args && app == &args->narg.next
9785 && !vars && !redir
9786 ) {
9787 struct builtincmd *bcmd;
9788 const char *name;
9789
9790 /* We have a function */
9791 if (readtoken() != TRP)
9792 raise_error_unexpected_syntax(TRP);
9793 name = n->narg.text;
9794 if (!goodname(name)
9795 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
9796 ) {
9797 raise_error_syntax("Bad function name");
9798 }
9799 n->type = NDEFUN;
9800 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9801 n->narg.next = parse_command();
9802 return n;
9803 }
9804 /* fall through */
9805 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009806 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009807 goto out;
9808 }
9809 }
9810 out:
9811 *app = NULL;
9812 *vpp = NULL;
9813 *rpp = NULL;
9814 n = stalloc(sizeof(struct ncmd));
9815 n->type = NCMD;
9816 n->ncmd.args = args;
9817 n->ncmd.assign = vars;
9818 n->ncmd.redirect = redir;
9819 return n;
9820}
9821
9822static union node *
9823parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009824{
Eric Andersencb57d552001-06-28 07:25:16 +00009825 union node *n1, *n2;
9826 union node *ap, **app;
9827 union node *cp, **cpp;
9828 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +00009829 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009830 int t;
9831
9832 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009833 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +00009834
Eric Andersencb57d552001-06-28 07:25:16 +00009835 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +00009836 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009837 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +00009838 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +00009839 case TIF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009840 n1 = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009841 n1->type = NIF;
9842 n1->nif.test = list(0);
9843 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009844 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009845 n1->nif.ifpart = list(0);
9846 n2 = n1;
9847 while (readtoken() == TELIF) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009848 n2->nif.elsepart = stalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +00009849 n2 = n2->nif.elsepart;
9850 n2->type = NIF;
9851 n2->nif.test = list(0);
9852 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009853 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +00009854 n2->nif.ifpart = list(0);
9855 }
9856 if (lasttoken == TELSE)
9857 n2->nif.elsepart = list(0);
9858 else {
9859 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009860 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009861 }
Eric Andersenc470f442003-07-28 09:56:35 +00009862 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +00009863 break;
9864 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +00009865 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +00009866 int got;
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009867 n1 = stalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009868 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +00009869 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009870 got = readtoken();
9871 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009872 TRACE(("expecting DO got %s %s\n", tokname(got),
9873 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009874 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009875 }
9876 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009877 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009878 break;
9879 }
9880 case TFOR:
Eric Andersenc470f442003-07-28 09:56:35 +00009881 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009882 raise_error_syntax("Bad for loop variable");
9883 n1 = stalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +00009884 n1->type = NFOR;
9885 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +00009886 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009887 if (readtoken() == TIN) {
9888 app = &ap;
9889 while (readtoken() == TWORD) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009890 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009891 n2->type = NARG;
9892 n2->narg.text = wordtext;
9893 n2->narg.backquote = backquotelist;
9894 *app = n2;
9895 app = &n2->narg.next;
9896 }
9897 *app = NULL;
9898 n1->nfor.args = ap;
9899 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009900 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +00009901 } else {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009902 n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009903 n2->type = NARG;
Eric Andersenc470f442003-07-28 09:56:35 +00009904 n2->narg.text = (char *)dolatstr;
Eric Andersencb57d552001-06-28 07:25:16 +00009905 n2->narg.backquote = NULL;
9906 n2->narg.next = NULL;
9907 n1->nfor.args = n2;
9908 /*
9909 * Newline or semicolon here is optional (but note
9910 * that the original Bourne shell only allowed NL).
9911 */
9912 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009913 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009914 }
Eric Andersenc470f442003-07-28 09:56:35 +00009915 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009916 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009917 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +00009918 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009919 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +00009920 break;
9921 case TCASE:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009922 n1 = stalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +00009923 n1->type = NCASE;
9924 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009925 raise_error_unexpected_syntax(TWORD);
9926 n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009927 n2->type = NARG;
9928 n2->narg.text = wordtext;
9929 n2->narg.backquote = backquotelist;
9930 n2->narg.next = NULL;
9931 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009932 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +00009933 } while (readtoken() == TNL);
9934 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009935 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +00009936 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009937 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +00009938 checkkwd = CHKNL | CHKKWD;
9939 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009940 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009941 if (lasttoken == TLP)
9942 readtoken();
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009943 *cpp = cp = stalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +00009944 cp->type = NCLIST;
9945 app = &cp->nclist.pattern;
9946 for (;;) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009947 *app = ap = stalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +00009948 ap->type = NARG;
9949 ap->narg.text = wordtext;
9950 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +00009951 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +00009952 break;
9953 app = &ap->narg.next;
9954 readtoken();
9955 }
9956 ap->narg.next = NULL;
9957 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009958 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +00009959 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +00009960
Eric Andersenc470f442003-07-28 09:56:35 +00009961 cpp = &cp->nclist.next;
9962
9963 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009964 t = readtoken();
9965 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +00009966 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009967 raise_error_unexpected_syntax(TENDCASE);
9968 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +00009969 }
Eric Andersenc470f442003-07-28 09:56:35 +00009970 }
Eric Andersencb57d552001-06-28 07:25:16 +00009971 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009972 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +00009973 case TLP:
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009974 n1 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +00009975 n1->type = NSUBSHELL;
9976 n1->nredir.n = list(0);
9977 n1->nredir.redirect = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009978 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +00009979 break;
9980 case TBEGIN:
9981 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +00009982 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009984 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +00009985 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009986 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009987 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +00009988 }
9989
Eric Andersenc470f442003-07-28 09:56:35 +00009990 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009991 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +00009992
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009993 redir:
Eric Andersencb57d552001-06-28 07:25:16 +00009994 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +00009995 checkkwd = CHKKWD | CHKALIAS;
9996 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +00009997 while (readtoken() == TREDIR) {
9998 *rpp = n2 = redirnode;
9999 rpp = &n2->nfile.next;
10000 parsefname();
10001 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010002 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010003 *rpp = NULL;
10004 if (redir) {
10005 if (n1->type != NSUBSHELL) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010006 n2 = stalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010007 n2->type = NREDIR;
10008 n2->nredir.n = n1;
10009 n1 = n2;
10010 }
10011 n1->nredir.redirect = redir;
10012 }
Eric Andersencb57d552001-06-28 07:25:16 +000010013 return n1;
10014}
10015
Eric Andersencb57d552001-06-28 07:25:16 +000010016/*
10017 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10018 * is not NULL, read a here document. In the latter case, eofmark is the
10019 * word which marks the end of the document and striptabs is true if
10020 * leading tabs should be stripped from the document. The argument firstc
10021 * is the first character of the input token or document.
10022 *
10023 * Because C does not have internal subroutines, I have simulated them
10024 * using goto's to implement the subroutine linkage. The following macros
10025 * will run code that appears at the end of readtoken1.
10026 */
10027
Eric Andersen2870d962001-07-02 17:27:21 +000010028#define CHECKEND() {goto checkend; checkend_return:;}
10029#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10030#define PARSESUB() {goto parsesub; parsesub_return:;}
10031#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10032#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10033#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010034
10035static int
Eric Andersenc470f442003-07-28 09:56:35 +000010036readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010037{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010038 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010039 int c = firstc;
10040 char *out;
10041 int len;
10042 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010043 struct nodelist *bqlist;
10044 smallint quotef;
10045 smallint dblquote;
10046 smallint oldstyle;
10047 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010048#if ENABLE_ASH_EXPAND_PRMT
10049 smallint pssyntax; /* we are expanding a prompt string */
10050#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010051 int varnest; /* levels of variables expansion */
10052 int arinest; /* levels of arithmetic expansion */
10053 int parenlevel; /* levels of parens in arithmetic */
10054 int dqvarnest; /* levels of variables expansion within double quotes */
10055
Eric Andersencb57d552001-06-28 07:25:16 +000010056#if __GNUC__
10057 /* Avoid longjmp clobbering */
10058 (void) &out;
10059 (void) &quotef;
10060 (void) &dblquote;
10061 (void) &varnest;
10062 (void) &arinest;
10063 (void) &parenlevel;
10064 (void) &dqvarnest;
10065 (void) &oldstyle;
10066 (void) &prevsyntax;
10067 (void) &syntax;
10068#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010069 startlinno = plinno;
Eric Andersencb57d552001-06-28 07:25:16 +000010070 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010071 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010072 oldstyle = 0;
10073 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010074#if ENABLE_ASH_EXPAND_PRMT
10075 pssyntax = (syntax == PSSYNTAX);
10076 if (pssyntax)
10077 syntax = DQSYNTAX;
10078#endif
10079 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010080 varnest = 0;
10081 arinest = 0;
10082 parenlevel = 0;
10083 dqvarnest = 0;
10084
10085 STARTSTACKSTR(out);
Eric Andersenc470f442003-07-28 09:56:35 +000010086 loop: { /* for each line, until end of word */
10087 CHECKEND(); /* set c to PEOF if at end of here document */
10088 for (;;) { /* until end of line or end of word */
10089 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010090 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010091 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010092 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010093 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010094 USTPUTC(c, out);
10095 plinno++;
10096 if (doprompt)
10097 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010098 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010099 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010100 case CWORD:
10101 USTPUTC(c, out);
10102 break;
10103 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010104 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010105 USTPUTC(CTLESC, out);
10106 USTPUTC(c, out);
10107 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010108 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010109 c = pgetc2();
10110 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010111 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010112 USTPUTC('\\', out);
10113 pungetc();
10114 } else if (c == '\n') {
10115 if (doprompt)
10116 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010117 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010118#if ENABLE_ASH_EXPAND_PRMT
10119 if (c == '$' && pssyntax) {
10120 USTPUTC(CTLESC, out);
10121 USTPUTC('\\', out);
10122 }
10123#endif
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010124 if (dblquote &&
Eric Andersenc470f442003-07-28 09:56:35 +000010125 c != '\\' && c != '`' &&
10126 c != '$' && (
10127 c != '"' ||
"Vladimir N. Oleynik"84005af2006-01-25 17:53:04 +000010128 eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010129 ) {
10130 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010131 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010132 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010133 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010134 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010135 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010136 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010137 }
10138 break;
10139 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010140 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010141 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010142 if (eofmark == NULL) {
10143 USTPUTC(CTLQUOTEMARK, out);
10144 }
Eric Andersencb57d552001-06-28 07:25:16 +000010145 break;
10146 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010147 syntax = DQSYNTAX;
10148 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010149 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010150 case CENDQUOTE:
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010151 if (eofmark != NULL && arinest == 0
10152 && varnest == 0
10153 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010154 USTPUTC(c, out);
10155 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010156 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010157 syntax = BASESYNTAX;
10158 dblquote = 0;
10159 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010160 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010161 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010162 }
10163 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010164 case CVAR: /* '$' */
10165 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010166 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010167 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010168 if (varnest > 0) {
10169 varnest--;
10170 if (dqvarnest > 0) {
10171 dqvarnest--;
10172 }
10173 USTPUTC(CTLENDVAR, out);
10174 } else {
10175 USTPUTC(c, out);
10176 }
10177 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010178#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010179 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010180 parenlevel++;
10181 USTPUTC(c, out);
10182 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010183 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010184 if (parenlevel > 0) {
10185 USTPUTC(c, out);
10186 --parenlevel;
10187 } else {
10188 if (pgetc() == ')') {
10189 if (--arinest == 0) {
10190 USTPUTC(CTLENDARI, out);
10191 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010192 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010193 } else
10194 USTPUTC(')', out);
10195 } else {
10196 /*
10197 * unbalanced parens
10198 * (don't 2nd guess - no error)
10199 */
10200 pungetc();
10201 USTPUTC(')', out);
10202 }
10203 }
10204 break;
10205#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010206 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010207 PARSEBACKQOLD();
10208 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010209 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010210 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010211 case CIGN:
10212 break;
10213 default:
10214 if (varnest == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010215 goto endword; /* exit outer loop */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010216#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010217 if (c != PEOA)
10218#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010219 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010220
Eric Andersencb57d552001-06-28 07:25:16 +000010221 }
10222 c = pgetc_macro();
10223 }
10224 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010225 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010226#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010227 if (syntax == ARISYNTAX)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010228 raise_error_syntax("Missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010229#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010230 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010231 raise_error_syntax("Unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010232 if (varnest != 0) {
10233 startlinno = plinno;
Eric Andersenc470f442003-07-28 09:56:35 +000010234 /* { */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010235 raise_error_syntax("Missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010236 }
10237 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010238 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010239 out = stackblock();
10240 if (eofmark == NULL) {
10241 if ((c == '>' || c == '<')
Eric Andersenc470f442003-07-28 09:56:35 +000010242 && quotef == 0
10243 && len <= 2
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010244 && (*out == '\0' || isdigit(*out))) {
Eric Andersencb57d552001-06-28 07:25:16 +000010245 PARSEREDIR();
10246 return lasttoken = TREDIR;
10247 } else {
10248 pungetc();
10249 }
10250 }
10251 quoteflag = quotef;
10252 backquotelist = bqlist;
10253 grabstackblock(len);
10254 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010255 lasttoken = TWORD;
10256 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010257/* end of readtoken routine */
10258
Eric Andersencb57d552001-06-28 07:25:16 +000010259/*
10260 * Check to see whether we are at the end of the here document. When this
10261 * is called, c is set to the first character of the next input line. If
10262 * we are at the end of the here document, this routine sets the c to PEOF.
10263 */
Eric Andersenc470f442003-07-28 09:56:35 +000010264checkend: {
10265 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010266#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010267 if (c == PEOA) {
10268 c = pgetc2();
10269 }
10270#endif
10271 if (striptabs) {
10272 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000010273 c = pgetc2();
10274 }
Eric Andersenc470f442003-07-28 09:56:35 +000010275 }
10276 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010277 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000010278 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010279
Eric Andersenc470f442003-07-28 09:56:35 +000010280 p = line;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010281 for (q = eofmark + 1; *q && *p == *q; p++, q++);
Eric Andersenc470f442003-07-28 09:56:35 +000010282 if (*p == '\n' && *q == '\0') {
10283 c = PEOF;
10284 plinno++;
10285 needprompt = doprompt;
10286 } else {
10287 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000010288 }
10289 }
10290 }
10291 }
Eric Andersenc470f442003-07-28 09:56:35 +000010292 goto checkend_return;
10293}
Eric Andersencb57d552001-06-28 07:25:16 +000010294
Eric Andersencb57d552001-06-28 07:25:16 +000010295/*
10296 * Parse a redirection operator. The variable "out" points to a string
10297 * specifying the fd to be redirected. The variable "c" contains the
10298 * first character of the redirection operator.
10299 */
Eric Andersenc470f442003-07-28 09:56:35 +000010300parseredir: {
10301 char fd = *out;
10302 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000010303
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010304 np = stalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000010305 if (c == '>') {
10306 np->nfile.fd = 1;
10307 c = pgetc();
10308 if (c == '>')
10309 np->type = NAPPEND;
10310 else if (c == '|')
10311 np->type = NCLOBBER;
10312 else if (c == '&')
10313 np->type = NTOFD;
10314 else {
10315 np->type = NTO;
10316 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010317 }
Eric Andersenc470f442003-07-28 09:56:35 +000010318 } else { /* c == '<' */
10319 np->nfile.fd = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010320 c = pgetc();
10321 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000010322 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010323 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010324 np = stalloc(sizeof(struct nhere));
Eric Andersenc470f442003-07-28 09:56:35 +000010325 np->nfile.fd = 0;
10326 }
10327 np->type = NHERE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010328 heredoc = stalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000010329 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010330 c = pgetc();
10331 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000010332 heredoc->striptabs = 1;
10333 } else {
10334 heredoc->striptabs = 0;
10335 pungetc();
10336 }
10337 break;
10338
10339 case '&':
10340 np->type = NFROMFD;
10341 break;
10342
10343 case '>':
10344 np->type = NFROMTO;
10345 break;
10346
10347 default:
10348 np->type = NFROM;
10349 pungetc();
10350 break;
10351 }
Eric Andersencb57d552001-06-28 07:25:16 +000010352 }
Eric Andersenc470f442003-07-28 09:56:35 +000010353 if (fd != '\0')
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010354 np->nfile.fd = fd - '0';
Eric Andersenc470f442003-07-28 09:56:35 +000010355 redirnode = np;
10356 goto parseredir_return;
10357}
Eric Andersencb57d552001-06-28 07:25:16 +000010358
Eric Andersencb57d552001-06-28 07:25:16 +000010359/*
10360 * Parse a substitution. At this point, we have read the dollar sign
10361 * and nothing else.
10362 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000010363
10364/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
10365 * (assuming ascii char codes, as the original implementation did) */
10366#define is_special(c) \
10367 ((((unsigned int)c) - 33 < 32) \
10368 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000010369parsesub: {
10370 int subtype;
10371 int typeloc;
10372 int flags;
10373 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010374 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000010375
Eric Andersenc470f442003-07-28 09:56:35 +000010376 c = pgetc();
10377 if (
10378 c <= PEOA_OR_PEOF ||
10379 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10380 ) {
10381 USTPUTC('$', out);
10382 pungetc();
10383 } else if (c == '(') { /* $(command) or $((arith)) */
10384 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010385#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010386 PARSEARITH();
10387#else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010388 raise_error_syntax("We unsupport $((arith))");
Eric Andersenc470f442003-07-28 09:56:35 +000010389#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010390 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010391 pungetc();
10392 PARSEBACKQNEW();
10393 }
10394 } else {
10395 USTPUTC(CTLVAR, out);
10396 typeloc = out - (char *)stackblock();
10397 USTPUTC(VSNORMAL, out);
10398 subtype = VSNORMAL;
10399 if (c == '{') {
10400 c = pgetc();
10401 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010402 c = pgetc();
10403 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000010404 c = '#';
10405 else
10406 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010407 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010408 subtype = 0;
10409 }
10410 if (c > PEOA_OR_PEOF && is_name(c)) {
10411 do {
10412 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010413 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010414 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010415 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010416 do {
10417 STPUTC(c, out);
10418 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010419 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010420 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010421 USTPUTC(c, out);
10422 c = pgetc();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010423 } else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010424 badsub: raise_error_syntax("Bad substitution");
Eric Andersencb57d552001-06-28 07:25:16 +000010425
Eric Andersenc470f442003-07-28 09:56:35 +000010426 STPUTC('=', out);
10427 flags = 0;
10428 if (subtype == 0) {
10429 switch (c) {
10430 case ':':
10431 flags = VSNUL;
10432 c = pgetc();
10433 /*FALLTHROUGH*/
10434 default:
10435 p = strchr(types, c);
10436 if (p == NULL)
10437 goto badsub;
10438 subtype = p - types + VSNORMAL;
10439 break;
10440 case '%':
10441 case '#':
Eric Andersencb57d552001-06-28 07:25:16 +000010442 {
10443 int cc = c;
Eric Andersenc470f442003-07-28 09:56:35 +000010444 subtype = c == '#' ? VSTRIMLEFT :
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010445 VSTRIMRIGHT;
Eric Andersencb57d552001-06-28 07:25:16 +000010446 c = pgetc();
10447 if (c == cc)
10448 subtype++;
10449 else
10450 pungetc();
10451 break;
10452 }
10453 }
Eric Andersenc470f442003-07-28 09:56:35 +000010454 } else {
10455 pungetc();
10456 }
10457 if (dblquote || arinest)
10458 flags |= VSQUOTE;
10459 *((char *)stackblock() + typeloc) = subtype | flags;
10460 if (subtype != VSNORMAL) {
10461 varnest++;
10462 if (dblquote || arinest) {
10463 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000010464 }
10465 }
10466 }
Eric Andersenc470f442003-07-28 09:56:35 +000010467 goto parsesub_return;
10468}
Eric Andersencb57d552001-06-28 07:25:16 +000010469
Eric Andersencb57d552001-06-28 07:25:16 +000010470/*
10471 * Called to parse command substitutions. Newstyle is set if the command
10472 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10473 * list of commands (passed by reference), and savelen is the number of
10474 * characters on the top of the stack which must be preserved.
10475 */
Eric Andersenc470f442003-07-28 09:56:35 +000010476parsebackq: {
10477 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010478 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000010479 union node *n;
10480 char *volatile str;
10481 struct jmploc jmploc;
10482 struct jmploc *volatile savehandler;
10483 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010484 smallint saveprompt = 0;
10485
Eric Andersencb57d552001-06-28 07:25:16 +000010486#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000010487 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000010488#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010489 savepbq = parsebackquote;
10490 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000010491 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010492 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010493 exception_handler = savehandler;
10494 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010495 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010496 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000010497 str = NULL;
10498 savelen = out - (char *)stackblock();
10499 if (savelen > 0) {
10500 str = ckmalloc(savelen);
10501 memcpy(str, stackblock(), savelen);
10502 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010503 savehandler = exception_handler;
10504 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010505 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010506 if (oldstyle) {
10507 /* We must read until the closing backquote, giving special
10508 treatment to some slashes, and then push the string and
10509 reread it as input, interpreting it normally. */
10510 char *pout;
10511 int pc;
10512 size_t psavelen;
10513 char *pstr;
10514
10515
10516 STARTSTACKSTR(pout);
10517 for (;;) {
10518 if (needprompt) {
10519 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000010520 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010521 pc = pgetc();
10522 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000010523 case '`':
10524 goto done;
10525
10526 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010527 pc = pgetc();
10528 if (pc == '\n') {
Eric Andersenc470f442003-07-28 09:56:35 +000010529 plinno++;
10530 if (doprompt)
10531 setprompt(2);
10532 /*
10533 * If eating a newline, avoid putting
10534 * the newline into the new character
10535 * stream (via the STPUTC after the
10536 * switch).
10537 */
10538 continue;
10539 }
10540 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010541 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000010542 STPUTC('\\', pout);
10543 if (pc > PEOA_OR_PEOF) {
10544 break;
10545 }
10546 /* fall through */
10547
10548 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010549#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000010550 case PEOA:
10551#endif
10552 startlinno = plinno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010553 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000010554
10555 case '\n':
10556 plinno++;
10557 needprompt = doprompt;
10558 break;
10559
10560 default:
10561 break;
10562 }
10563 STPUTC(pc, pout);
10564 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010565 done:
Eric Andersenc470f442003-07-28 09:56:35 +000010566 STPUTC('\0', pout);
10567 psavelen = pout - (char *)stackblock();
10568 if (psavelen > 0) {
10569 pstr = grabstackstr(pout);
10570 setinputstring(pstr);
10571 }
10572 }
10573 nlpp = &bqlist;
10574 while (*nlpp)
10575 nlpp = &(*nlpp)->next;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010576 *nlpp = stalloc(sizeof(**nlpp));
Eric Andersenc470f442003-07-28 09:56:35 +000010577 (*nlpp)->next = NULL;
10578 parsebackquote = oldstyle;
10579
10580 if (oldstyle) {
10581 saveprompt = doprompt;
10582 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010583 }
10584
Eric Andersenc470f442003-07-28 09:56:35 +000010585 n = list(2);
10586
10587 if (oldstyle)
10588 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010589 else if (readtoken() != TRP)
10590 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010591
10592 (*nlpp)->n = n;
10593 if (oldstyle) {
10594 /*
10595 * Start reading from old file again, ignoring any pushed back
10596 * tokens left from the backquote parsing
10597 */
10598 popfile();
10599 tokpushback = 0;
10600 }
10601 while (stackblocksize() <= savelen)
10602 growstackblock();
10603 STARTSTACKSTR(out);
10604 if (str) {
10605 memcpy(out, str, savelen);
10606 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010607 INT_OFF;
10608 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000010609 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010610 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010611 }
10612 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010613 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010614 if (arinest || dblquote)
10615 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10616 else
10617 USTPUTC(CTLBACKQ, out);
10618 if (oldstyle)
10619 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010620 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000010621}
10622
Denis Vlasenko131ae172007-02-18 13:00:19 +000010623#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010624/*
10625 * Parse an arithmetic expansion (indicate start of one and set state)
10626 */
Eric Andersenc470f442003-07-28 09:56:35 +000010627parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000010628 if (++arinest == 1) {
10629 prevsyntax = syntax;
10630 syntax = ARISYNTAX;
10631 USTPUTC(CTLARI, out);
10632 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010633 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010634 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010635 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010636 } else {
10637 /*
10638 * we collapse embedded arithmetic expansion to
10639 * parenthesis, which should be equivalent
10640 */
10641 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000010642 }
Eric Andersenc470f442003-07-28 09:56:35 +000010643 goto parsearith_return;
10644}
10645#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010646
Eric Andersenc470f442003-07-28 09:56:35 +000010647} /* end of readtoken */
10648
Eric Andersencb57d552001-06-28 07:25:16 +000010649/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010650 * Read the next input token.
10651 * If the token is a word, we set backquotelist to the list of cmds in
10652 * backquotes. We set quoteflag to true if any part of the word was
10653 * quoted.
10654 * If the token is TREDIR, then we set redirnode to a structure containing
10655 * the redirection.
10656 * In all cases, the variable startlinno is set to the number of the line
10657 * on which the token starts.
10658 *
10659 * [Change comment: here documents and internal procedures]
10660 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10661 * word parsing code into a separate routine. In this case, readtoken
10662 * doesn't need to have any internal procedures, but parseword does.
10663 * We could also make parseoperator in essence the main routine, and
10664 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000010665 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010666#define NEW_xxreadtoken
10667#ifdef NEW_xxreadtoken
10668/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010669static const char xxreadtoken_chars[7] ALIGN1 = {
10670 '\n', '(', ')', '&', '|', ';', 0
10671};
Eric Andersencb57d552001-06-28 07:25:16 +000010672
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010673static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010674 TNL, TLP, TRP, /* only single occurrence allowed */
10675 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10676 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000010677 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010678};
10679
10680#define xxreadtoken_doubles \
10681 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10682#define xxreadtoken_singles \
10683 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10684
10685static int
10686xxreadtoken(void)
10687{
10688 int c;
10689
10690 if (tokpushback) {
10691 tokpushback = 0;
10692 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000010693 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010694 if (needprompt) {
10695 setprompt(2);
10696 }
10697 startlinno = plinno;
10698 for (;;) { /* until token or start of word found */
10699 c = pgetc_macro();
10700
10701 if ((c != ' ') && (c != '\t')
10702#if ENABLE_ASH_ALIAS
10703 && (c != PEOA)
10704#endif
10705 ) {
10706 if (c == '#') {
10707 while ((c = pgetc()) != '\n' && c != PEOF);
10708 pungetc();
10709 } else if (c == '\\') {
10710 if (pgetc() != '\n') {
10711 pungetc();
10712 goto READTOKEN1;
10713 }
10714 startlinno = ++plinno;
10715 if (doprompt)
10716 setprompt(2);
10717 } else {
10718 const char *p
10719 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10720
10721 if (c != PEOF) {
10722 if (c == '\n') {
10723 plinno++;
10724 needprompt = doprompt;
10725 }
10726
10727 p = strchr(xxreadtoken_chars, c);
10728 if (p == NULL) {
10729 READTOKEN1:
10730 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10731 }
10732
10733 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10734 if (pgetc() == *p) { /* double occurrence? */
10735 p += xxreadtoken_doubles + 1;
10736 } else {
10737 pungetc();
10738 }
10739 }
10740 }
10741 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10742 }
10743 }
10744 } /* for */
10745}
10746#else
10747#define RETURN(token) return lasttoken = token
10748static int
10749xxreadtoken(void)
10750{
10751 int c;
10752
10753 if (tokpushback) {
10754 tokpushback = 0;
10755 return lasttoken;
10756 }
10757 if (needprompt) {
10758 setprompt(2);
10759 }
10760 startlinno = plinno;
10761 for (;;) { /* until token or start of word found */
10762 c = pgetc_macro();
10763 switch (c) {
10764 case ' ': case '\t':
10765#if ENABLE_ASH_ALIAS
10766 case PEOA:
10767#endif
10768 continue;
10769 case '#':
10770 while ((c = pgetc()) != '\n' && c != PEOF);
10771 pungetc();
10772 continue;
10773 case '\\':
10774 if (pgetc() == '\n') {
10775 startlinno = ++plinno;
10776 if (doprompt)
10777 setprompt(2);
10778 continue;
10779 }
10780 pungetc();
10781 goto breakloop;
10782 case '\n':
10783 plinno++;
10784 needprompt = doprompt;
10785 RETURN(TNL);
10786 case PEOF:
10787 RETURN(TEOF);
10788 case '&':
10789 if (pgetc() == '&')
10790 RETURN(TAND);
10791 pungetc();
10792 RETURN(TBACKGND);
10793 case '|':
10794 if (pgetc() == '|')
10795 RETURN(TOR);
10796 pungetc();
10797 RETURN(TPIPE);
10798 case ';':
10799 if (pgetc() == ';')
10800 RETURN(TENDCASE);
10801 pungetc();
10802 RETURN(TSEMI);
10803 case '(':
10804 RETURN(TLP);
10805 case ')':
10806 RETURN(TRP);
10807 default:
10808 goto breakloop;
10809 }
10810 }
10811 breakloop:
10812 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10813#undef RETURN
10814}
10815#endif /* NEW_xxreadtoken */
10816
10817static int
10818readtoken(void)
10819{
10820 int t;
10821#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010822 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010823#endif
10824
10825#if ENABLE_ASH_ALIAS
10826 top:
10827#endif
10828
10829 t = xxreadtoken();
10830
10831 /*
10832 * eat newlines
10833 */
10834 if (checkkwd & CHKNL) {
10835 while (t == TNL) {
10836 parseheredoc();
10837 t = xxreadtoken();
10838 }
10839 }
10840
10841 if (t != TWORD || quoteflag) {
10842 goto out;
10843 }
10844
10845 /*
10846 * check for keywords
10847 */
10848 if (checkkwd & CHKKWD) {
10849 const char *const *pp;
10850
10851 pp = findkwd(wordtext);
10852 if (pp) {
10853 lasttoken = t = pp - tokname_array;
10854 TRACE(("keyword %s recognized\n", tokname(t)));
10855 goto out;
10856 }
10857 }
10858
10859 if (checkkwd & CHKALIAS) {
10860#if ENABLE_ASH_ALIAS
10861 struct alias *ap;
10862 ap = lookupalias(wordtext, 1);
10863 if (ap != NULL) {
10864 if (*ap->val) {
10865 pushstring(ap->val, ap);
10866 }
10867 goto top;
10868 }
10869#endif
10870 }
10871 out:
10872 checkkwd = 0;
10873#if DEBUG
10874 if (!alreadyseen)
10875 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10876 else
10877 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
10878#endif
10879 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000010880}
10881
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010882static char
10883peektoken(void)
10884{
10885 int t;
10886
10887 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010888 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010889 return tokname_array[t][0];
10890}
Eric Andersencb57d552001-06-28 07:25:16 +000010891
10892/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010893 * Read and parse a command. Returns NEOF on end of file. (NULL is a
10894 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000010895 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010896static union node *
10897parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000010898{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010899 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000010900
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010901 tokpushback = 0;
10902 doprompt = interact;
10903 if (doprompt)
10904 setprompt(doprompt);
10905 needprompt = 0;
10906 t = readtoken();
10907 if (t == TEOF)
10908 return NEOF;
10909 if (t == TNL)
10910 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010911 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010912 return list(1);
10913}
10914
10915/*
10916 * Input any here documents.
10917 */
10918static void
10919parseheredoc(void)
10920{
10921 struct heredoc *here;
10922 union node *n;
10923
10924 here = heredoclist;
10925 heredoclist = 0;
10926
10927 while (here) {
10928 if (needprompt) {
10929 setprompt(2);
10930 }
10931 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
10932 here->eofmark, here->striptabs);
10933 n = stalloc(sizeof(struct narg));
10934 n->narg.type = NARG;
10935 n->narg.next = NULL;
10936 n->narg.text = wordtext;
10937 n->narg.backquote = backquotelist;
10938 here->here->nhere.doc = n;
10939 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000010940 }
Eric Andersencb57d552001-06-28 07:25:16 +000010941}
10942
10943
10944/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010945 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000010946 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000010947#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010948static const char *
10949expandstr(const char *ps)
10950{
10951 union node n;
10952
10953 /* XXX Fix (char *) cast. */
10954 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000010955 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000010956 popfile();
10957
10958 n.narg.type = NARG;
10959 n.narg.next = NULL;
10960 n.narg.text = wordtext;
10961 n.narg.backquote = backquotelist;
10962
10963 expandarg(&n, NULL, 0);
10964 return stackblock();
10965}
10966#endif
10967
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010968/*
10969 * Execute a command or commands contained in a string.
10970 */
10971static int
10972evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000010973{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010974 union node *n;
10975 struct stackmark smark;
10976 int skip;
10977
10978 setinputstring(s);
10979 setstackmark(&smark);
10980
10981 skip = 0;
10982 while ((n = parsecmd(0)) != NEOF) {
10983 evaltree(n, 0);
10984 popstackmark(&smark);
10985 skip = evalskip;
10986 if (skip)
10987 break;
10988 }
10989 popfile();
10990
10991 skip &= mask;
10992 evalskip = skip;
10993 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000010994}
10995
Denis Vlasenko0c032a42007-02-23 01:03:40 +000010996/*
10997 * The eval command.
10998 */
10999static int
11000evalcmd(int argc, char **argv)
11001{
11002 char *p;
11003 char *concat;
11004 char **ap;
11005
11006 if (argc > 1) {
11007 p = argv[1];
11008 if (argc > 2) {
11009 STARTSTACKSTR(concat);
11010 ap = argv + 2;
11011 for (;;) {
11012 concat = stack_putstr(p, concat);
11013 p = *ap++;
11014 if (p == NULL)
11015 break;
11016 STPUTC(' ', concat);
11017 }
11018 STPUTC('\0', concat);
11019 p = grabstackstr(concat);
11020 }
11021 evalstring(p, ~SKIPEVAL);
11022
11023 }
11024 return exitstatus;
11025}
11026
11027/*
11028 * Read and execute commands. "Top" is nonzero for the top level command
11029 * loop; it turns on prompting if the shell is interactive.
11030 */
11031static int
11032cmdloop(int top)
11033{
11034 union node *n;
11035 struct stackmark smark;
11036 int inter;
11037 int numeof = 0;
11038
11039 TRACE(("cmdloop(%d) called\n", top));
11040 for (;;) {
11041 int skip;
11042
11043 setstackmark(&smark);
11044#if JOBS
11045 if (jobctl)
11046 showjobs(stderr, SHOW_CHANGED);
11047#endif
11048 inter = 0;
11049 if (iflag && top) {
11050 inter++;
11051#if ENABLE_ASH_MAIL
11052 chkmail();
11053#endif
11054 }
11055 n = parsecmd(inter);
11056 /* showtree(n); DEBUG */
11057 if (n == NEOF) {
11058 if (!top || numeof >= 50)
11059 break;
11060 if (!stoppedjobs()) {
11061 if (!Iflag)
11062 break;
11063 out2str("\nUse \"exit\" to leave shell.\n");
11064 }
11065 numeof++;
11066 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011067 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11068 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011069 numeof = 0;
11070 evaltree(n, 0);
11071 }
11072 popstackmark(&smark);
11073 skip = evalskip;
11074
11075 if (skip) {
11076 evalskip = 0;
11077 return skip & SKIPEVAL;
11078 }
11079 }
11080 return 0;
11081}
11082
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011083/*
11084 * Take commands from a file. To be compatible we should do a path
11085 * search for the file, which is necessary to find sub-commands.
11086 */
11087static char *
11088find_dot_file(char *name)
11089{
11090 char *fullname;
11091 const char *path = pathval();
11092 struct stat statb;
11093
11094 /* don't try this for absolute or relative paths */
11095 if (strchr(name, '/'))
11096 return name;
11097
11098 while ((fullname = padvance(&path, name)) != NULL) {
11099 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11100 /*
11101 * Don't bother freeing here, since it will
11102 * be freed by the caller.
11103 */
11104 return fullname;
11105 }
11106 stunalloc(fullname);
11107 }
11108
11109 /* not found in the PATH */
11110 ash_msg_and_raise_error("%s: not found", name);
11111 /* NOTREACHED */
11112}
11113
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011114static int
11115dotcmd(int argc, char **argv)
11116{
11117 struct strlist *sp;
11118 volatile struct shparam saveparam;
11119 int status = 0;
11120
11121 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011122 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011123
11124 if (argc >= 2) { /* That's what SVR2 does */
11125 char *fullname;
11126
11127 fullname = find_dot_file(argv[1]);
11128
11129 if (argc > 2) {
11130 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011131 shellparam.malloced = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011132 shellparam.nparam = argc - 2;
11133 shellparam.p = argv + 2;
11134 };
11135
11136 setinputfile(fullname, INPUT_PUSH_FILE);
11137 commandname = fullname;
11138 cmdloop(0);
11139 popfile();
11140
11141 if (argc > 2) {
11142 freeparam(&shellparam);
11143 shellparam = saveparam;
11144 };
11145 status = exitstatus;
11146 }
11147 return status;
11148}
11149
11150static int
11151exitcmd(int argc, char **argv)
11152{
11153 if (stoppedjobs())
11154 return 0;
11155 if (argc > 1)
11156 exitstatus = number(argv[1]);
11157 raise_exception(EXEXIT);
11158 /* NOTREACHED */
11159}
11160
11161#if ENABLE_ASH_BUILTIN_ECHO
11162static int
11163echocmd(int argc, char **argv)
11164{
Denis Vlasenkofe5e23b2007-11-24 02:23:51 +000011165 return echo_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011166}
11167#endif
11168
11169#if ENABLE_ASH_BUILTIN_TEST
11170static int
11171testcmd(int argc, char **argv)
11172{
Denis Vlasenkob304ead2007-06-21 13:35:52 +000011173 return test_main(argc, argv);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011174}
11175#endif
11176
11177/*
11178 * Read a file containing shell functions.
11179 */
11180static void
11181readcmdfile(char *name)
11182{
11183 setinputfile(name, INPUT_PUSH_FILE);
11184 cmdloop(0);
11185 popfile();
11186}
11187
11188
Denis Vlasenkocc571512007-02-23 21:10:35 +000011189/* ============ find_command inplementation */
11190
11191/*
11192 * Resolve a command name. If you change this routine, you may have to
11193 * change the shellexec routine as well.
11194 */
11195static void
11196find_command(char *name, struct cmdentry *entry, int act, const char *path)
11197{
11198 struct tblentry *cmdp;
11199 int idx;
11200 int prev;
11201 char *fullname;
11202 struct stat statb;
11203 int e;
11204 int updatetbl;
11205 struct builtincmd *bcmd;
11206
11207 /* If name contains a slash, don't use PATH or hash table */
11208 if (strchr(name, '/') != NULL) {
11209 entry->u.index = -1;
11210 if (act & DO_ABS) {
11211 while (stat(name, &statb) < 0) {
11212#ifdef SYSV
11213 if (errno == EINTR)
11214 continue;
11215#endif
11216 entry->cmdtype = CMDUNKNOWN;
11217 return;
11218 }
11219 }
11220 entry->cmdtype = CMDNORMAL;
11221 return;
11222 }
11223
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011224/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011225
11226 updatetbl = (path == pathval());
11227 if (!updatetbl) {
11228 act |= DO_ALTPATH;
11229 if (strstr(path, "%builtin") != NULL)
11230 act |= DO_ALTBLTIN;
11231 }
11232
11233 /* If name is in the table, check answer will be ok */
11234 cmdp = cmdlookup(name, 0);
11235 if (cmdp != NULL) {
11236 int bit;
11237
11238 switch (cmdp->cmdtype) {
11239 default:
11240#if DEBUG
11241 abort();
11242#endif
11243 case CMDNORMAL:
11244 bit = DO_ALTPATH;
11245 break;
11246 case CMDFUNCTION:
11247 bit = DO_NOFUNC;
11248 break;
11249 case CMDBUILTIN:
11250 bit = DO_ALTBLTIN;
11251 break;
11252 }
11253 if (act & bit) {
11254 updatetbl = 0;
11255 cmdp = NULL;
11256 } else if (cmdp->rehash == 0)
11257 /* if not invalidated by cd, we're done */
11258 goto success;
11259 }
11260
11261 /* If %builtin not in path, check for builtin next */
11262 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011263 if (bcmd) {
11264 if (IS_BUILTIN_REGULAR(bcmd))
11265 goto builtin_success;
11266 if (act & DO_ALTPATH) {
11267 if (!(act & DO_ALTBLTIN))
11268 goto builtin_success;
11269 } else if (builtinloc <= 0) {
11270 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000011271 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011272 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000011273
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011274#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011275 if (find_applet_by_name(name) >= 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000011276 entry->cmdtype = CMDNORMAL;
11277 entry->u.index = -1;
11278 return;
11279 }
11280#endif
11281
Denis Vlasenkocc571512007-02-23 21:10:35 +000011282 /* We have to search path. */
11283 prev = -1; /* where to start */
11284 if (cmdp && cmdp->rehash) { /* doing a rehash */
11285 if (cmdp->cmdtype == CMDBUILTIN)
11286 prev = builtinloc;
11287 else
11288 prev = cmdp->param.index;
11289 }
11290
11291 e = ENOENT;
11292 idx = -1;
11293 loop:
11294 while ((fullname = padvance(&path, name)) != NULL) {
11295 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011296 /* NB: code below will still use fullname
11297 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011298 idx++;
11299 if (pathopt) {
11300 if (prefix(pathopt, "builtin")) {
11301 if (bcmd)
11302 goto builtin_success;
11303 continue;
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011304 } else if (!(act & DO_NOFUNC)
11305 && prefix(pathopt, "func")) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000011306 /* handled below */
11307 } else {
11308 /* ignore unimplemented options */
11309 continue;
11310 }
11311 }
11312 /* if rehash, don't redo absolute path names */
11313 if (fullname[0] == '/' && idx <= prev) {
11314 if (idx < prev)
11315 continue;
11316 TRACE(("searchexec \"%s\": no change\n", name));
11317 goto success;
11318 }
11319 while (stat(fullname, &statb) < 0) {
11320#ifdef SYSV
11321 if (errno == EINTR)
11322 continue;
11323#endif
11324 if (errno != ENOENT && errno != ENOTDIR)
11325 e = errno;
11326 goto loop;
11327 }
11328 e = EACCES; /* if we fail, this will be the error */
11329 if (!S_ISREG(statb.st_mode))
11330 continue;
11331 if (pathopt) { /* this is a %func directory */
11332 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000011333 /* NB: stalloc will return space pointed by fullname
11334 * (because we don't have any intervening allocations
11335 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011336 readcmdfile(fullname);
11337 cmdp = cmdlookup(name, 0);
11338 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
11339 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
11340 stunalloc(fullname);
11341 goto success;
11342 }
11343 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
11344 if (!updatetbl) {
11345 entry->cmdtype = CMDNORMAL;
11346 entry->u.index = idx;
11347 return;
11348 }
11349 INT_OFF;
11350 cmdp = cmdlookup(name, 1);
11351 cmdp->cmdtype = CMDNORMAL;
11352 cmdp->param.index = idx;
11353 INT_ON;
11354 goto success;
11355 }
11356
11357 /* We failed. If there was an entry for this command, delete it */
11358 if (cmdp && updatetbl)
11359 delete_cmd_entry();
11360 if (act & DO_ERR)
11361 ash_msg("%s: %s", name, errmsg(e, "not found"));
11362 entry->cmdtype = CMDUNKNOWN;
11363 return;
11364
11365 builtin_success:
11366 if (!updatetbl) {
11367 entry->cmdtype = CMDBUILTIN;
11368 entry->u.cmd = bcmd;
11369 return;
11370 }
11371 INT_OFF;
11372 cmdp = cmdlookup(name, 1);
11373 cmdp->cmdtype = CMDBUILTIN;
11374 cmdp->param.cmd = bcmd;
11375 INT_ON;
11376 success:
11377 cmdp->rehash = 0;
11378 entry->cmdtype = cmdp->cmdtype;
11379 entry->u = cmdp->param;
11380}
11381
11382
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011383/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000011384
Eric Andersencb57d552001-06-28 07:25:16 +000011385/*
Eric Andersencb57d552001-06-28 07:25:16 +000011386 * The trap builtin.
11387 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011388static int
Eric Andersenc470f442003-07-28 09:56:35 +000011389trapcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011390{
11391 char *action;
11392 char **ap;
11393 int signo;
11394
Eric Andersenc470f442003-07-28 09:56:35 +000011395 nextopt(nullstr);
11396 ap = argptr;
11397 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011398 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000011399 if (trap[signo] != NULL) {
Eric Andersen34506362001-08-02 05:02:46 +000011400 const char *sn;
Eric Andersencb57d552001-06-28 07:25:16 +000011401
Rob Landleyc9c1a412006-07-12 19:17:55 +000011402 sn = get_signame(signo);
Eric Andersenc470f442003-07-28 09:56:35 +000011403 out1fmt("trap -- %s %s\n",
11404 single_quote(trap[signo]), sn);
Eric Andersencb57d552001-06-28 07:25:16 +000011405 }
11406 }
11407 return 0;
11408 }
Eric Andersenc470f442003-07-28 09:56:35 +000011409 if (!ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011410 action = NULL;
11411 else
11412 action = *ap++;
11413 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011414 signo = get_signum(*ap);
11415 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011416 ash_msg_and_raise_error("%s: bad trap", *ap);
11417 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011418 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000011419 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000011420 action = NULL;
11421 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011422 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Denis Vlasenko60818682007-09-28 22:07:23 +000011424 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000011425 trap[signo] = action;
11426 if (signo != 0)
11427 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011428 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011429 ap++;
11430 }
11431 return 0;
11432}
11433
Eric Andersenc470f442003-07-28 09:56:35 +000011434
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011435/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000011436
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000011437#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011438/*
11439 * Lists available builtins
11440 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011441static int
11442helpcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011443{
11444 int col, i;
11445
11446 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000011447 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011448 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000011449 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011450 if (col > 60) {
11451 out1fmt("\n");
11452 col = 0;
11453 }
11454 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000011455#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000011456 {
11457 const char *a = applet_names;
11458 while (*a) {
11459 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
11460 if (col > 60) {
11461 out1fmt("\n");
11462 col = 0;
11463 }
11464 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011465 }
11466 }
11467#endif
11468 out1fmt("\n\n");
11469 return EXIT_SUCCESS;
11470}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011471#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000011472
Eric Andersencb57d552001-06-28 07:25:16 +000011473/*
Eric Andersencb57d552001-06-28 07:25:16 +000011474 * The export and readonly commands.
11475 */
Eric Andersenc470f442003-07-28 09:56:35 +000011476static int
11477exportcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011478{
11479 struct var *vp;
11480 char *name;
11481 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000011482 char **aptr;
11483 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000011484
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011485 if (nextopt("p") != 'p') {
11486 aptr = argptr;
11487 name = *aptr;
11488 if (name) {
11489 do {
11490 p = strchr(name, '=');
11491 if (p != NULL) {
11492 p++;
11493 } else {
11494 vp = *findvar(hashvar(name), name);
11495 if (vp) {
11496 vp->flags |= flag;
11497 continue;
11498 }
Eric Andersencb57d552001-06-28 07:25:16 +000011499 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011500 setvar(name, p, flag);
11501 } while ((name = *++aptr) != NULL);
11502 return 0;
11503 }
Eric Andersencb57d552001-06-28 07:25:16 +000011504 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011505 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000011506 return 0;
11507}
11508
Eric Andersencb57d552001-06-28 07:25:16 +000011509/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011510 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000011511 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011512static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011513unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000011514{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011515 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000011516
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000011517 cmdp = cmdlookup(name, 0);
11518 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
11519 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000011520}
11521
Eric Andersencb57d552001-06-28 07:25:16 +000011522/*
Eric Andersencb57d552001-06-28 07:25:16 +000011523 * The unset builtin command. We unset the function before we unset the
11524 * variable to allow a function to be unset when there is a readonly variable
11525 * with the same name.
11526 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011527static int
Eric Andersenc470f442003-07-28 09:56:35 +000011528unsetcmd(int argc, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011529{
11530 char **ap;
11531 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000011532 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011533 int ret = 0;
11534
11535 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000011536 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000011537 }
Eric Andersencb57d552001-06-28 07:25:16 +000011538
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011539 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000011540 if (flag != 'f') {
11541 i = unsetvar(*ap);
11542 ret |= i;
11543 if (!(i & 2))
11544 continue;
11545 }
11546 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000011547 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000011548 }
Eric Andersenc470f442003-07-28 09:56:35 +000011549 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011550}
11551
11552
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011553/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000011554
Eric Andersenc470f442003-07-28 09:56:35 +000011555#include <sys/times.h>
11556
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011557static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011558 ' ', offsetof(struct tms, tms_utime),
11559 '\n', offsetof(struct tms, tms_stime),
11560 ' ', offsetof(struct tms, tms_cutime),
11561 '\n', offsetof(struct tms, tms_cstime),
11562 0
11563};
Eric Andersencb57d552001-06-28 07:25:16 +000011564
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011565static int
11566timescmd(int ac, char **av)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011567{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011568 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011569 const unsigned char *p;
11570 struct tms buf;
11571
11572 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000011573 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000011574
11575 p = timescmd_str;
11576 do {
11577 t = *(clock_t *)(((char *) &buf) + p[1]);
11578 s = t / clk_tck;
11579 out1fmt("%ldm%ld.%.3lds%c",
11580 s/60, s%60,
11581 ((t - s * clk_tck) * 1000) / clk_tck,
11582 p[0]);
11583 } while (*(p += 2));
11584
Eric Andersencb57d552001-06-28 07:25:16 +000011585 return 0;
11586}
11587
Denis Vlasenko131ae172007-02-18 13:00:19 +000011588#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000011589static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000011590dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000011591{
Eric Andersened9ecf72004-06-22 08:29:45 +000011592 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000011593 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011594
Denis Vlasenkob012b102007-02-19 22:43:01 +000011595 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011596 result = arith(s, &errcode);
11597 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000011598 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011599 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011600 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011601 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011602 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011603 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011604 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000011605 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011606 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011607
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011608 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000011609}
Eric Andersenc470f442003-07-28 09:56:35 +000011610
Eric Andersenc470f442003-07-28 09:56:35 +000011611/*
Eric Andersen90898442003-08-06 11:20:52 +000011612 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
11613 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
11614 *
11615 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000011616 */
11617static int
Eric Andersen90898442003-08-06 11:20:52 +000011618letcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011619{
Eric Andersenc470f442003-07-28 09:56:35 +000011620 char **ap;
Denis Vlasenkocba9ef52006-10-10 21:00:47 +000011621 arith_t i = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000011622
Eric Andersen90898442003-08-06 11:20:52 +000011623 ap = argv + 1;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011624 if (!*ap)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011625 ash_msg_and_raise_error("expression expected");
Eric Andersen90898442003-08-06 11:20:52 +000011626 for (ap = argv + 1; *ap; ap++) {
11627 i = dash_arith(*ap);
11628 }
Eric Andersenc470f442003-07-28 09:56:35 +000011629
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000011630 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000011631}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011632#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000011633
Eric Andersenc470f442003-07-28 09:56:35 +000011634
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011635/* ============ miscbltin.c
11636 *
Eric Andersenaff114c2004-04-14 17:51:38 +000011637 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000011638 */
11639
11640#undef rflag
11641
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000011642#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000011643typedef enum __rlimit_resource rlim_t;
11644#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000011645
Eric Andersenc470f442003-07-28 09:56:35 +000011646/*
11647 * The read builtin. The -e option causes backslashes to escape the
11648 * following character.
11649 *
11650 * This uses unbuffered input, which may be avoidable in some cases.
11651 */
Eric Andersenc470f442003-07-28 09:56:35 +000011652static int
11653readcmd(int argc, char **argv)
11654{
11655 char **ap;
11656 int backslash;
11657 char c;
11658 int rflag;
11659 char *prompt;
11660 const char *ifs;
11661 char *p;
11662 int startword;
11663 int status;
11664 int i;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011665#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011666 int n_flag = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000011667 int nchars = 0;
11668 int silent = 0;
11669 struct termios tty, old_tty;
11670#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011671#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011672 fd_set set;
11673 struct timeval ts;
11674
11675 ts.tv_sec = ts.tv_usec = 0;
11676#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011677
11678 rflag = 0;
11679 prompt = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011680#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011681 while ((i = nextopt("p:rt:n:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011682#elif ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011683 while ((i = nextopt("p:rn:s")) != '\0')
Denis Vlasenko131ae172007-02-18 13:00:19 +000011684#elif ENABLE_ASH_READ_TIMEOUT
Ned Ludd2123b7c2005-02-09 21:07:23 +000011685 while ((i = nextopt("p:rt:")) != '\0')
11686#else
11687 while ((i = nextopt("p:r")) != '\0')
11688#endif
11689 {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000011690 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000011691 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000011692 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000011693 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011694#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000011695 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011696 nchars = bb_strtou(optionarg, NULL, 10);
11697 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011698 ash_msg_and_raise_error("invalid count");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011699 n_flag = nchars; /* just a flag "nchars is nonzero" */
Paul Fox02eb9342005-09-07 16:56:02 +000011700 break;
11701 case 's':
11702 silent = 1;
11703 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000011704#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011705#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011706 case 't':
Denis Vlasenko037576d2007-10-20 18:30:38 +000011707 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000011708 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011709 /* EINVAL means number is ok, but not terminated by NUL */
11710 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000011711 char *p2;
11712 if (*++p) {
11713 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000011714 ts.tv_usec = bb_strtou(p, &p2, 10);
11715 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011716 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011717 scale = p2 - p;
11718 /* normalize to usec */
11719 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011720 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011721 while (scale++ < 6)
11722 ts.tv_usec *= 10;
11723 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011724 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000011725 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000011726 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011727 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000011728 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000011729 }
Paul Fox02eb9342005-09-07 16:56:02 +000011730 break;
11731#endif
11732 case 'r':
11733 rflag = 1;
11734 break;
11735 default:
11736 break;
11737 }
Eric Andersenc470f442003-07-28 09:56:35 +000011738 }
11739 if (prompt && isatty(0)) {
11740 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000011741 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011742 ap = argptr;
11743 if (*ap == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011744 ash_msg_and_raise_error("arg count");
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011745 ifs = bltinlookup("IFS");
11746 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000011747 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011748#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011749 if (n_flag || silent) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011750 if (tcgetattr(0, &tty) != 0) {
11751 /* Not a tty */
11752 n_flag = 0;
11753 silent = 0;
11754 } else {
11755 old_tty = tty;
11756 if (n_flag) {
11757 tty.c_lflag &= ~ICANON;
11758 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
11759 }
11760 if (silent) {
11761 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
11762 }
11763 tcsetattr(0, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000011764 }
Paul Fox02eb9342005-09-07 16:56:02 +000011765 }
11766#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000011767#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000011768 if (ts.tv_sec || ts.tv_usec) {
Denis Vlasenkod67cef22007-06-13 06:47:47 +000011769 FD_ZERO(&set);
11770 FD_SET(0, &set);
Paul Fox02eb9342005-09-07 16:56:02 +000011771
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011772 /* poll-based wait produces bigger code, using select */
11773 i = select(1, &set, NULL, NULL, &ts);
11774 if (!i) { /* timed out! */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011775#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011776 if (n_flag)
Paul Fox02eb9342005-09-07 16:56:02 +000011777 tcsetattr(0, TCSANOW, &old_tty);
11778#endif
11779 return 1;
11780 }
11781 }
Ned Ludd2123b7c2005-02-09 21:07:23 +000011782#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011783 status = 0;
11784 startword = 1;
11785 backslash = 0;
11786 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000011787 do {
Eric Andersenc470f442003-07-28 09:56:35 +000011788 if (read(0, &c, 1) != 1) {
11789 status = 1;
11790 break;
11791 }
11792 if (c == '\0')
11793 continue;
11794 if (backslash) {
11795 backslash = 0;
11796 if (c != '\n')
11797 goto put;
11798 continue;
11799 }
11800 if (!rflag && c == '\\') {
11801 backslash++;
11802 continue;
11803 }
11804 if (c == '\n')
11805 break;
11806 if (startword && *ifs == ' ' && strchr(ifs, c)) {
11807 continue;
11808 }
11809 startword = 0;
11810 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
11811 STACKSTRNUL(p);
11812 setvar(*ap, stackblock(), 0);
11813 ap++;
11814 startword = 1;
11815 STARTSTACKSTR(p);
11816 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011817 put:
Eric Andersenc470f442003-07-28 09:56:35 +000011818 STPUTC(c, p);
11819 }
11820 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000011821/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011822#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko037576d2007-10-20 18:30:38 +000011823 while (!n_flag || --nchars);
11824#else
11825 while (1);
11826#endif
11827
11828#if ENABLE_ASH_READ_NCHARS
11829 if (n_flag || silent)
Paul Fox02eb9342005-09-07 16:56:02 +000011830 tcsetattr(0, TCSANOW, &old_tty);
11831#endif
11832
Eric Andersenc470f442003-07-28 09:56:35 +000011833 STACKSTRNUL(p);
11834 /* Remove trailing blanks */
11835 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
11836 *p = '\0';
11837 setvar(*ap, stackblock(), 0);
11838 while (*++ap != NULL)
11839 setvar(*ap, nullstr, 0);
11840 return status;
11841}
11842
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000011843static int
11844umaskcmd(int argc, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000011845{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011846 static const char permuser[3] ALIGN1 = "ugo";
11847 static const char permmode[3] ALIGN1 = "rwx";
11848 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000011849 S_IRUSR, S_IWUSR, S_IXUSR,
11850 S_IRGRP, S_IWGRP, S_IXGRP,
11851 S_IROTH, S_IWOTH, S_IXOTH
11852 };
11853
11854 char *ap;
11855 mode_t mask;
11856 int i;
11857 int symbolic_mode = 0;
11858
11859 while (nextopt("S") != '\0') {
11860 symbolic_mode = 1;
11861 }
11862
Denis Vlasenkob012b102007-02-19 22:43:01 +000011863 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011864 mask = umask(0);
11865 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011866 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011867
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011868 ap = *argptr;
11869 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011870 if (symbolic_mode) {
11871 char buf[18];
11872 char *p = buf;
11873
11874 for (i = 0; i < 3; i++) {
11875 int j;
11876
11877 *p++ = permuser[i];
11878 *p++ = '=';
11879 for (j = 0; j < 3; j++) {
11880 if ((mask & permmask[3 * i + j]) == 0) {
11881 *p++ = permmode[j];
11882 }
11883 }
11884 *p++ = ',';
11885 }
11886 *--p = 0;
11887 puts(buf);
11888 } else {
11889 out1fmt("%.4o\n", mask);
11890 }
11891 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011892 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011893 mask = 0;
11894 do {
11895 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000011896 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000011897 mask = (mask << 3) + (*ap - '0');
11898 } while (*++ap != '\0');
11899 umask(mask);
11900 } else {
11901 mask = ~mask & 0777;
11902 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011903 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000011904 }
11905 umask(~mask & 0777);
11906 }
11907 }
11908 return 0;
11909}
11910
11911/*
11912 * ulimit builtin
11913 *
11914 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
11915 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
11916 * ash by J.T. Conklin.
11917 *
11918 * Public domain.
11919 */
11920
11921struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011922 uint8_t cmd; /* RLIMIT_xxx fit into it */
11923 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000011924 char option;
11925};
11926
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011927static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000011928#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011929 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000011930#endif
11931#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011932 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000011933#endif
11934#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011935 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000011936#endif
11937#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011938 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000011939#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011940#ifdef RLIMIT_CORE
11941 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000011942#endif
11943#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011944 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000011945#endif
11946#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011947 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000011948#endif
11949#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011950 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000011951#endif
11952#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011953 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000011954#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011955#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011956 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000011957#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000011958#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011959 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000011960#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011961};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000011962static const char limits_name[] =
11963#ifdef RLIMIT_CPU
11964 "time(seconds)" "\0"
11965#endif
11966#ifdef RLIMIT_FSIZE
11967 "file(blocks)" "\0"
11968#endif
11969#ifdef RLIMIT_DATA
11970 "data(kb)" "\0"
11971#endif
11972#ifdef RLIMIT_STACK
11973 "stack(kb)" "\0"
11974#endif
11975#ifdef RLIMIT_CORE
11976 "coredump(blocks)" "\0"
11977#endif
11978#ifdef RLIMIT_RSS
11979 "memory(kb)" "\0"
11980#endif
11981#ifdef RLIMIT_MEMLOCK
11982 "locked memory(kb)" "\0"
11983#endif
11984#ifdef RLIMIT_NPROC
11985 "process" "\0"
11986#endif
11987#ifdef RLIMIT_NOFILE
11988 "nofiles" "\0"
11989#endif
11990#ifdef RLIMIT_AS
11991 "vmemory(kb)" "\0"
11992#endif
11993#ifdef RLIMIT_LOCKS
11994 "locks" "\0"
11995#endif
11996;
Eric Andersenc470f442003-07-28 09:56:35 +000011997
Glenn L McGrath76620622004-01-13 10:19:37 +000011998enum limtype { SOFT = 0x1, HARD = 0x2 };
11999
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012000static void
12001printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012002 const struct limits *l)
12003{
12004 rlim_t val;
12005
12006 val = limit->rlim_max;
12007 if (how & SOFT)
12008 val = limit->rlim_cur;
12009
12010 if (val == RLIM_INFINITY)
12011 out1fmt("unlimited\n");
12012 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012013 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012014 out1fmt("%lld\n", (long long) val);
12015 }
12016}
12017
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012018static int
Eric Andersenc470f442003-07-28 09:56:35 +000012019ulimitcmd(int argc, char **argv)
12020{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012021 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012022 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012023 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012024 const struct limits *l;
12025 int set, all = 0;
12026 int optc, what;
12027 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012028
12029 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012030 while ((optc = nextopt("HSa"
12031#ifdef RLIMIT_CPU
12032 "t"
12033#endif
12034#ifdef RLIMIT_FSIZE
12035 "f"
12036#endif
12037#ifdef RLIMIT_DATA
12038 "d"
12039#endif
12040#ifdef RLIMIT_STACK
12041 "s"
12042#endif
12043#ifdef RLIMIT_CORE
12044 "c"
12045#endif
12046#ifdef RLIMIT_RSS
12047 "m"
12048#endif
12049#ifdef RLIMIT_MEMLOCK
12050 "l"
12051#endif
12052#ifdef RLIMIT_NPROC
12053 "p"
12054#endif
12055#ifdef RLIMIT_NOFILE
12056 "n"
12057#endif
12058#ifdef RLIMIT_AS
12059 "v"
12060#endif
12061#ifdef RLIMIT_LOCKS
12062 "w"
12063#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012064 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012065 switch (optc) {
12066 case 'H':
12067 how = HARD;
12068 break;
12069 case 'S':
12070 how = SOFT;
12071 break;
12072 case 'a':
12073 all = 1;
12074 break;
12075 default:
12076 what = optc;
12077 }
12078
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012079 for (l = limits_tbl; l->option != what; l++)
12080 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012081
12082 set = *argptr ? 1 : 0;
12083 if (set) {
12084 char *p = *argptr;
12085
12086 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012087 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012088 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012089 val = RLIM_INFINITY;
12090 else {
12091 val = (rlim_t) 0;
12092
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012093 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012094 val = (val * 10) + (long)(c - '0');
12095 if (val < (rlim_t) 0)
12096 break;
12097 }
12098 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012099 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012100 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012101 }
12102 }
12103 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012104 const char *lname = limits_name;
12105 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012106 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012107 out1fmt("%-20s ", lname);
12108 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012109 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012110 }
12111 return 0;
12112 }
12113
12114 getrlimit(l->cmd, &limit);
12115 if (set) {
12116 if (how & HARD)
12117 limit.rlim_max = val;
12118 if (how & SOFT)
12119 limit.rlim_cur = val;
12120 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012121 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012122 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012123 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012124 }
12125 return 0;
12126}
12127
Eric Andersen90898442003-08-06 11:20:52 +000012128
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012129/* ============ Math support */
12130
Denis Vlasenko131ae172007-02-18 13:00:19 +000012131#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012132
12133/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12134
12135 Permission is hereby granted, free of charge, to any person obtaining
12136 a copy of this software and associated documentation files (the
12137 "Software"), to deal in the Software without restriction, including
12138 without limitation the rights to use, copy, modify, merge, publish,
12139 distribute, sublicense, and/or sell copies of the Software, and to
12140 permit persons to whom the Software is furnished to do so, subject to
12141 the following conditions:
12142
12143 The above copyright notice and this permission notice shall be
12144 included in all copies or substantial portions of the Software.
12145
12146 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12147 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12148 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12149 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12150 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12151 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12152 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12153*/
12154
12155/* This is my infix parser/evaluator. It is optimized for size, intended
12156 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012157 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012158 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012159 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012160 * be that which POSIX specifies for shells. */
12161
12162/* The code uses a simple two-stack algorithm. See
12163 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012164 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012165 * this is based (this code differs in that it applies operators immediately
12166 * to the stack instead of adding them to a queue to end up with an
12167 * expression). */
12168
12169/* To use the routine, call it with an expression string and error return
12170 * pointer */
12171
12172/*
12173 * Aug 24, 2001 Manuel Novoa III
12174 *
12175 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12176 *
12177 * 1) In arith_apply():
12178 * a) Cached values of *numptr and &(numptr[-1]).
12179 * b) Removed redundant test for zero denominator.
12180 *
12181 * 2) In arith():
12182 * a) Eliminated redundant code for processing operator tokens by moving
12183 * to a table-based implementation. Also folded handling of parens
12184 * into the table.
12185 * b) Combined all 3 loops which called arith_apply to reduce generated
12186 * code size at the cost of speed.
12187 *
12188 * 3) The following expressions were treated as valid by the original code:
12189 * 1() , 0! , 1 ( *3 ) .
12190 * These bugs have been fixed by internally enclosing the expression in
12191 * parens and then checking that all binary ops and right parens are
12192 * preceded by a valid expression (NUM_TOKEN).
12193 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012194 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012195 * ctype's isspace() if it is used by another busybox applet or if additional
12196 * whitespace chars should be considered. Look below the "#include"s for a
12197 * precompiler test.
12198 */
12199
12200/*
12201 * Aug 26, 2001 Manuel Novoa III
12202 *
12203 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12204 *
12205 * Merge in Aaron's comments previously posted to the busybox list,
12206 * modified slightly to take account of my changes to the code.
12207 *
12208 */
12209
12210/*
12211 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12212 *
12213 * - allow access to variable,
12214 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12215 * - realize assign syntax (VAR=expr, +=, *= etc)
12216 * - realize exponentiation (** operator)
12217 * - realize comma separated - expr, expr
12218 * - realise ++expr --expr expr++ expr--
12219 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000012220 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000012221 * - was restored loses XOR operator
12222 * - remove one goto label, added three ;-)
12223 * - protect $((num num)) as true zero expr (Manuel`s error)
12224 * - always use special isspace(), see comment from bash ;-)
12225 */
12226
Eric Andersen90898442003-08-06 11:20:52 +000012227#define arith_isspace(arithval) \
12228 (arithval == ' ' || arithval == '\n' || arithval == '\t')
12229
Eric Andersen90898442003-08-06 11:20:52 +000012230typedef unsigned char operator;
12231
12232/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000012233 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000012234 * precedence. The ID portion is so that multiple operators can have the
12235 * same precedence, ensuring that the leftmost one is evaluated first.
12236 * Consider * and /. */
12237
12238#define tok_decl(prec,id) (((id)<<5)|(prec))
12239#define PREC(op) ((op) & 0x1F)
12240
12241#define TOK_LPAREN tok_decl(0,0)
12242
12243#define TOK_COMMA tok_decl(1,0)
12244
12245#define TOK_ASSIGN tok_decl(2,0)
12246#define TOK_AND_ASSIGN tok_decl(2,1)
12247#define TOK_OR_ASSIGN tok_decl(2,2)
12248#define TOK_XOR_ASSIGN tok_decl(2,3)
12249#define TOK_PLUS_ASSIGN tok_decl(2,4)
12250#define TOK_MINUS_ASSIGN tok_decl(2,5)
12251#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
12252#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
12253
12254#define TOK_MUL_ASSIGN tok_decl(3,0)
12255#define TOK_DIV_ASSIGN tok_decl(3,1)
12256#define TOK_REM_ASSIGN tok_decl(3,2)
12257
12258/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012259#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000012260
12261/* conditional is right associativity too */
12262#define TOK_CONDITIONAL tok_decl(4,0)
12263#define TOK_CONDITIONAL_SEP tok_decl(4,1)
12264
12265#define TOK_OR tok_decl(5,0)
12266
12267#define TOK_AND tok_decl(6,0)
12268
12269#define TOK_BOR tok_decl(7,0)
12270
12271#define TOK_BXOR tok_decl(8,0)
12272
12273#define TOK_BAND tok_decl(9,0)
12274
12275#define TOK_EQ tok_decl(10,0)
12276#define TOK_NE tok_decl(10,1)
12277
12278#define TOK_LT tok_decl(11,0)
12279#define TOK_GT tok_decl(11,1)
12280#define TOK_GE tok_decl(11,2)
12281#define TOK_LE tok_decl(11,3)
12282
12283#define TOK_LSHIFT tok_decl(12,0)
12284#define TOK_RSHIFT tok_decl(12,1)
12285
12286#define TOK_ADD tok_decl(13,0)
12287#define TOK_SUB tok_decl(13,1)
12288
12289#define TOK_MUL tok_decl(14,0)
12290#define TOK_DIV tok_decl(14,1)
12291#define TOK_REM tok_decl(14,2)
12292
12293/* exponent is right associativity */
12294#define TOK_EXPONENT tok_decl(15,1)
12295
12296/* For now unary operators. */
12297#define UNARYPREC 16
12298#define TOK_BNOT tok_decl(UNARYPREC,0)
12299#define TOK_NOT tok_decl(UNARYPREC,1)
12300
12301#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
12302#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
12303
12304#define PREC_PRE (UNARYPREC+2)
12305
12306#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
12307#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
12308
12309#define PREC_POST (UNARYPREC+3)
12310
12311#define TOK_POST_INC tok_decl(PREC_POST, 0)
12312#define TOK_POST_DEC tok_decl(PREC_POST, 1)
12313
12314#define SPEC_PREC (UNARYPREC+4)
12315
12316#define TOK_NUM tok_decl(SPEC_PREC, 0)
12317#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
12318
12319#define NUMPTR (*numstackptr)
12320
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012321static int
12322tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000012323{
12324 operator prec = PREC(op);
12325
12326 convert_prec_is_assing(prec);
12327 return (prec == PREC(TOK_ASSIGN) ||
12328 prec == PREC_PRE || prec == PREC_POST);
12329}
12330
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012331static int
12332is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000012333{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012334 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
12335 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000012336}
12337
Eric Andersen90898442003-08-06 11:20:52 +000012338typedef struct ARITCH_VAR_NUM {
Eric Andersened9ecf72004-06-22 08:29:45 +000012339 arith_t val;
12340 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000012341 char contidional_second_val_initialized;
12342 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000012343 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000012344} v_n_t;
12345
Eric Andersen90898442003-08-06 11:20:52 +000012346typedef struct CHK_VAR_RECURSIVE_LOOPED {
12347 const char *var;
12348 struct CHK_VAR_RECURSIVE_LOOPED *next;
12349} chk_var_recursive_looped_t;
12350
12351static chk_var_recursive_looped_t *prev_chk_var_recursive;
12352
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012353static int
12354arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000012355{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012356 if (t->var) {
12357 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000012358
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012359 if (p) {
12360 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012361
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012362 /* recursive try as expression */
12363 chk_var_recursive_looped_t *cur;
12364 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000012365
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012366 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
12367 if (strcmp(cur->var, t->var) == 0) {
12368 /* expression recursion loop detected */
12369 return -5;
12370 }
12371 }
12372 /* save current lookuped var name */
12373 cur = prev_chk_var_recursive;
12374 cur_save.var = t->var;
12375 cur_save.next = cur;
12376 prev_chk_var_recursive = &cur_save;
12377
12378 t->val = arith (p, &errcode);
12379 /* restore previous ptr after recursiving */
12380 prev_chk_var_recursive = cur;
12381 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012382 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012383 /* allow undefined var as 0 */
12384 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012385 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012386 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000012387}
12388
12389/* "applying" a token means performing it on the top elements on the integer
12390 * stack. For a unary operator it will only change the top element, but a
12391 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012392static int
12393arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000012394{
Eric Andersen90898442003-08-06 11:20:52 +000012395 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000012396 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000012397 int ret_arith_lookup_val;
12398
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012399 /* There is no operator that can work without arguments */
12400 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012401 numptr_m1 = NUMPTR - 1;
12402
12403 /* check operand is var with noninteger value */
12404 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012405 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000012406 return ret_arith_lookup_val;
12407
12408 rez = numptr_m1->val;
12409 if (op == TOK_UMINUS)
12410 rez *= -1;
12411 else if (op == TOK_NOT)
12412 rez = !rez;
12413 else if (op == TOK_BNOT)
12414 rez = ~rez;
12415 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
12416 rez++;
12417 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
12418 rez--;
12419 else if (op != TOK_UPLUS) {
12420 /* Binary operators */
12421
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012422 /* check and binary operators need two arguments */
12423 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012424
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012425 /* ... and they pop one */
12426 --NUMPTR;
12427 numptr_val = rez;
12428 if (op == TOK_CONDITIONAL) {
12429 if (! numptr_m1->contidional_second_val_initialized) {
12430 /* protect $((expr1 ? expr2)) without ": expr" */
12431 goto err;
12432 }
12433 rez = numptr_m1->contidional_second_val;
12434 } else if (numptr_m1->contidional_second_val_initialized) {
12435 /* protect $((expr1 : expr2)) without "expr ? " */
12436 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012437 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012438 numptr_m1 = NUMPTR - 1;
12439 if (op != TOK_ASSIGN) {
12440 /* check operand is var with noninteger value for not '=' */
12441 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
12442 if (ret_arith_lookup_val)
12443 return ret_arith_lookup_val;
12444 }
12445 if (op == TOK_CONDITIONAL) {
12446 numptr_m1->contidional_second_val = rez;
12447 }
12448 rez = numptr_m1->val;
12449 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012450 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012451 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000012452 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012453 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012454 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012455 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012456 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012457 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000012458 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012459 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000012460 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012461 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000012462 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012463 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000012464 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012465 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012466 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012467 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012468 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012469 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000012470 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012471 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000012472 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012473 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000012474 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012475 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012476 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012477 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012478 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012479 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012480 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012481 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000012482 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012483 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000012484 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012485 /* protect $((expr : expr)) without "expr ? " */
12486 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000012487 }
12488 numptr_m1->contidional_second_val_initialized = op;
12489 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012490 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000012491 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012492 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012493 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012494 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000012495 return -3; /* exponent less than 0 */
12496 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000012497 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000012498
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012499 if (numptr_val)
12500 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000012501 c *= rez;
12502 rez = c;
12503 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012504 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000012505 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012506 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012507 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012508 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000012509 rez %= numptr_val;
12510 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012511 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012512 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000012513
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012514 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000012515 /* Hmm, 1=2 ? */
12516 goto err;
12517 }
12518 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012519#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012520 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012521#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012522 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012523#endif
Eric Andersen90898442003-08-06 11:20:52 +000012524 setvar(numptr_m1->var, buf, 0);
12525 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012526 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000012527 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012528 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000012529 rez++;
12530 }
12531 numptr_m1->val = rez;
12532 /* protect geting var value, is number now */
12533 numptr_m1->var = NULL;
12534 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000012535 err:
12536 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000012537}
12538
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012539/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012540static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000012541 '<','<','=',0, TOK_LSHIFT_ASSIGN,
12542 '>','>','=',0, TOK_RSHIFT_ASSIGN,
12543 '<','<', 0, TOK_LSHIFT,
12544 '>','>', 0, TOK_RSHIFT,
12545 '|','|', 0, TOK_OR,
12546 '&','&', 0, TOK_AND,
12547 '!','=', 0, TOK_NE,
12548 '<','=', 0, TOK_LE,
12549 '>','=', 0, TOK_GE,
12550 '=','=', 0, TOK_EQ,
12551 '|','=', 0, TOK_OR_ASSIGN,
12552 '&','=', 0, TOK_AND_ASSIGN,
12553 '*','=', 0, TOK_MUL_ASSIGN,
12554 '/','=', 0, TOK_DIV_ASSIGN,
12555 '%','=', 0, TOK_REM_ASSIGN,
12556 '+','=', 0, TOK_PLUS_ASSIGN,
12557 '-','=', 0, TOK_MINUS_ASSIGN,
12558 '-','-', 0, TOK_POST_DEC,
12559 '^','=', 0, TOK_XOR_ASSIGN,
12560 '+','+', 0, TOK_POST_INC,
12561 '*','*', 0, TOK_EXPONENT,
12562 '!', 0, TOK_NOT,
12563 '<', 0, TOK_LT,
12564 '>', 0, TOK_GT,
12565 '=', 0, TOK_ASSIGN,
12566 '|', 0, TOK_BOR,
12567 '&', 0, TOK_BAND,
12568 '*', 0, TOK_MUL,
12569 '/', 0, TOK_DIV,
12570 '%', 0, TOK_REM,
12571 '+', 0, TOK_ADD,
12572 '-', 0, TOK_SUB,
12573 '^', 0, TOK_BXOR,
12574 /* uniq */
12575 '~', 0, TOK_BNOT,
12576 ',', 0, TOK_COMMA,
12577 '?', 0, TOK_CONDITIONAL,
12578 ':', 0, TOK_CONDITIONAL_SEP,
12579 ')', 0, TOK_RPAREN,
12580 '(', 0, TOK_LPAREN,
12581 0
12582};
12583/* ptr to ")" */
12584#define endexpression &op_tokens[sizeof(op_tokens)-7]
12585
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012586static arith_t
12587arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000012588{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012589 char arithval; /* Current character under analysis */
12590 operator lasttok, op;
12591 operator prec;
Eric Andersen90898442003-08-06 11:20:52 +000012592
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012593 const char *p = endexpression;
12594 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000012595
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012596 size_t datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000012597
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012598 /* Stack of integers */
12599 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
12600 * in any given correct or incorrect expression is left as an exercise to
12601 * the reader. */
12602 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
12603 *numstackptr = numstack;
12604 /* Stack of operator tokens */
12605 operator *stack = alloca((datasizes) * sizeof(operator)),
12606 *stackptr = stack;
Eric Andersen90898442003-08-06 11:20:52 +000012607
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012608 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
12609 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000012610
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012611 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012612 arithval = *expr;
12613 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012614 if (p == endexpression) {
12615 /* Null expression. */
12616 return 0;
12617 }
12618
12619 /* This is only reached after all tokens have been extracted from the
12620 * input stream. If there are still tokens on the operator stack, they
12621 * are to be applied in order. At the end, there should be a final
12622 * result on the integer stack */
12623
12624 if (expr != endexpression + 1) {
12625 /* If we haven't done so already, */
12626 /* append a closing right paren */
12627 expr = endexpression;
12628 /* and let the loop process it. */
12629 continue;
12630 }
12631 /* At this point, we're done with the expression. */
12632 if (numstackptr != numstack+1) {
12633 /* ... but if there isn't, it's bad */
12634 err:
12635 return (*perrcode = -1);
12636 }
12637 if (numstack->var) {
12638 /* expression is $((var)) only, lookup now */
12639 errcode = arith_lookup_val(numstack);
12640 }
12641 ret:
12642 *perrcode = errcode;
12643 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000012644 }
12645
Eric Andersen90898442003-08-06 11:20:52 +000012646 /* Continue processing the expression. */
12647 if (arith_isspace(arithval)) {
12648 /* Skip whitespace */
12649 goto prologue;
12650 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012651 p = endofname(expr);
12652 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000012653 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000012654
12655 numstackptr->var = alloca(var_name_size);
12656 safe_strncpy(numstackptr->var, expr, var_name_size);
12657 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012658 num:
Eric Andersen90898442003-08-06 11:20:52 +000012659 numstackptr->contidional_second_val_initialized = 0;
12660 numstackptr++;
12661 lasttok = TOK_NUM;
12662 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012663 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012664 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000012665 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012666#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000012667 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012668#else
12669 numstackptr->val = strtol(expr, (char **) &expr, 0);
12670#endif
Eric Andersen90898442003-08-06 11:20:52 +000012671 goto num;
12672 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012673 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000012674 const char *o;
12675
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012676 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012677 /* strange operator not found */
12678 goto err;
12679 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012680 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000012681 o++;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012682 if (! *p) {
Eric Andersen90898442003-08-06 11:20:52 +000012683 /* found */
12684 expr = o - 1;
12685 break;
12686 }
12687 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012688 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000012689 p++;
12690 /* skip zero delim */
12691 p++;
12692 }
12693 op = p[1];
12694
12695 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012696 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
12697 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000012698
12699 /* Plus and minus are binary (not unary) _only_ if the last
12700 * token was as number, or a right paren (which pretends to be
12701 * a number, since it evaluates to one). Think about it.
12702 * It makes sense. */
12703 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012704 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012705 case TOK_ADD:
12706 op = TOK_UPLUS;
12707 break;
12708 case TOK_SUB:
12709 op = TOK_UMINUS;
12710 break;
12711 case TOK_POST_INC:
12712 op = TOK_PRE_INC;
12713 break;
12714 case TOK_POST_DEC:
12715 op = TOK_PRE_DEC;
12716 break;
Eric Andersen90898442003-08-06 11:20:52 +000012717 }
12718 }
12719 /* We don't want a unary operator to cause recursive descent on the
12720 * stack, because there can be many in a row and it could cause an
12721 * operator to be evaluated before its argument is pushed onto the
12722 * integer stack. */
12723 /* But for binary operators, "apply" everything on the operator
12724 * stack until we find an operator with a lesser priority than the
12725 * one we have just extracted. */
12726 /* Left paren is given the lowest priority so it will never be
12727 * "applied" in this way.
12728 * if associativity is right and priority eq, applied also skip
12729 */
12730 prec = PREC(op);
12731 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
12732 /* not left paren or unary */
12733 if (lasttok != TOK_NUM) {
12734 /* binary op must be preceded by a num */
12735 goto err;
12736 }
12737 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012738 if (op == TOK_RPAREN) {
12739 /* The algorithm employed here is simple: while we don't
12740 * hit an open paren nor the bottom of the stack, pop
12741 * tokens and apply them */
12742 if (stackptr[-1] == TOK_LPAREN) {
12743 --stackptr;
12744 /* Any operator directly after a */
12745 lasttok = TOK_NUM;
12746 /* close paren should consider itself binary */
12747 goto prologue;
12748 }
12749 } else {
12750 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000012751
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012752 convert_prec_is_assing(prec);
12753 convert_prec_is_assing(prev_prec);
12754 if (prev_prec < prec)
12755 break;
12756 /* check right assoc */
12757 if (prev_prec == prec && is_right_associativity(prec))
12758 break;
12759 }
12760 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
12761 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000012762 }
12763 if (op == TOK_RPAREN) {
12764 goto err;
12765 }
12766 }
12767
12768 /* Push this operator to the stack and remember it. */
12769 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012770 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000012771 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012772 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000012773}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012774#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000012775
12776
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012777/* ============ main() and helpers */
12778
12779/*
12780 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012781 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012782static void exitshell(void) ATTRIBUTE_NORETURN;
12783static void
12784exitshell(void)
12785{
12786 struct jmploc loc;
12787 char *p;
12788 int status;
12789
12790 status = exitstatus;
12791 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12792 if (setjmp(loc.loc)) {
12793 if (exception == EXEXIT)
12794/* dash bug: it just does _exit(exitstatus) here
12795 * but we have to do setjobctl(0) first!
12796 * (bug is still not fixed in dash-0.5.3 - if you run dash
12797 * under Midnight Commander, on exit from dash MC is backgrounded) */
12798 status = exitstatus;
12799 goto out;
12800 }
12801 exception_handler = &loc;
12802 p = trap[0];
12803 if (p) {
12804 trap[0] = NULL;
12805 evalstring(p, 0);
12806 }
12807 flush_stdout_stderr();
12808 out:
12809 setjobctl(0);
12810 _exit(status);
12811 /* NOTREACHED */
12812}
12813
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012814static void
12815init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012816{
12817 /* from input.c: */
12818 basepf.nextc = basepf.buf = basebuf;
12819
12820 /* from trap.c: */
12821 signal(SIGCHLD, SIG_DFL);
12822
12823 /* from var.c: */
12824 {
12825 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012826 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012827 const char *p;
12828 struct stat st1, st2;
12829
12830 initvar();
12831 for (envp = environ; envp && *envp; envp++) {
12832 if (strchr(*envp, '=')) {
12833 setvareq(*envp, VEXPORT|VTEXTFIXED);
12834 }
12835 }
12836
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012837 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012838 setvar("PPID", ppid, 0);
12839
12840 p = lookupvar("PWD");
12841 if (p)
12842 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12843 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12844 p = '\0';
12845 setpwd(p, 0);
12846 }
12847}
12848
12849/*
12850 * Process the shell command line arguments.
12851 */
12852static void
12853procargs(int argc, char **argv)
12854{
12855 int i;
12856 const char *xminusc;
12857 char **xargv;
12858
12859 xargv = argv;
12860 arg0 = xargv[0];
12861 if (argc > 0)
12862 xargv++;
12863 for (i = 0; i < NOPTS; i++)
12864 optlist[i] = 2;
12865 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012866 if (options(1)) {
12867 /* it already printed err message */
12868 raise_exception(EXERROR);
12869 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012870 xargv = argptr;
12871 xminusc = minusc;
12872 if (*xargv == NULL) {
12873 if (xminusc)
12874 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12875 sflag = 1;
12876 }
12877 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12878 iflag = 1;
12879 if (mflag == 2)
12880 mflag = iflag;
12881 for (i = 0; i < NOPTS; i++)
12882 if (optlist[i] == 2)
12883 optlist[i] = 0;
12884#if DEBUG == 2
12885 debug = 1;
12886#endif
12887 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12888 if (xminusc) {
12889 minusc = *xargv++;
12890 if (*xargv)
12891 goto setarg0;
12892 } else if (!sflag) {
12893 setinputfile(*xargv, 0);
12894 setarg0:
12895 arg0 = *xargv++;
12896 commandname = arg0;
12897 }
12898
12899 shellparam.p = xargv;
12900#if ENABLE_ASH_GETOPTS
12901 shellparam.optind = 1;
12902 shellparam.optoff = -1;
12903#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012904 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012905 while (*xargv) {
12906 shellparam.nparam++;
12907 xargv++;
12908 }
12909 optschanged();
12910}
12911
12912/*
12913 * Read /etc/profile or .profile.
12914 */
12915static void
12916read_profile(const char *name)
12917{
12918 int skip;
12919
12920 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12921 return;
12922 skip = cmdloop(0);
12923 popfile();
12924 if (skip)
12925 exitshell();
12926}
12927
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012928/*
12929 * This routine is called when an error or an interrupt occurs in an
12930 * interactive shell and control is returned to the main command loop.
12931 */
12932static void
12933reset(void)
12934{
12935 /* from eval.c: */
12936 evalskip = 0;
12937 loopnest = 0;
12938 /* from input.c: */
12939 parselleft = parsenleft = 0; /* clear input buffer */
12940 popallfiles();
12941 /* from parser.c: */
12942 tokpushback = 0;
12943 checkkwd = 0;
12944 /* from redir.c: */
12945 clearredir(0);
12946}
12947
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012948#if PROFILE
12949static short profile_buf[16384];
12950extern int etext();
12951#endif
12952
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012953/*
12954 * Main routine. We initialize things, parse the arguments, execute
12955 * profiles if we're a login shell, and then call cmdloop to execute
12956 * commands. The setjmp call sets up the location to jump to when an
12957 * exception occurs. When an exception occurs the variable "state"
12958 * is used to figure out how far we had gotten.
12959 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012960int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012961int ash_main(int argc, char **argv)
12962{
12963 char *shinit;
12964 volatile int state;
12965 struct jmploc jmploc;
12966 struct stackmark smark;
12967
Denis Vlasenko01631112007-12-16 17:20:38 +000012968 /* Initialize global data */
12969 INIT_G_misc();
12970 INIT_G_memstack();
12971 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012972#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000012973 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000012974#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012975 INIT_G_cmdtable();
12976
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012977#if PROFILE
12978 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
12979#endif
12980
12981#if ENABLE_FEATURE_EDITING
12982 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
12983#endif
12984 state = 0;
12985 if (setjmp(jmploc.loc)) {
12986 int e;
12987 int s;
12988
12989 reset();
12990
12991 e = exception;
12992 if (e == EXERROR)
12993 exitstatus = 2;
12994 s = state;
12995 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
12996 exitshell();
12997
12998 if (e == EXINT) {
12999 outcslow('\n', stderr);
13000 }
13001 popstackmark(&smark);
13002 FORCE_INT_ON; /* enable interrupts */
13003 if (s == 1)
13004 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013005 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013007 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013008 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013009 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013010 }
13011 exception_handler = &jmploc;
13012#if DEBUG
13013 opentrace();
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013014 trace_puts("Shell args: ");
13015 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013016#endif
13017 rootpid = getpid();
13018
13019#if ENABLE_ASH_RANDOM_SUPPORT
13020 rseed = rootpid + time(NULL);
13021#endif
13022 init();
13023 setstackmark(&smark);
13024 procargs(argc, argv);
13025#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13026 if (iflag) {
13027 const char *hp = lookupvar("HISTFILE");
13028
13029 if (hp == NULL) {
13030 hp = lookupvar("HOME");
13031 if (hp != NULL) {
13032 char *defhp = concat_path_file(hp, ".ash_history");
13033 setvar("HISTFILE", defhp, 0);
13034 free(defhp);
13035 }
13036 }
13037 }
13038#endif
13039 if (argv[0] && argv[0][0] == '-')
13040 isloginsh = 1;
13041 if (isloginsh) {
13042 state = 1;
13043 read_profile("/etc/profile");
13044 state1:
13045 state = 2;
13046 read_profile(".profile");
13047 }
13048 state2:
13049 state = 3;
13050 if (
13051#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013052 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013053#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013054 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013055 ) {
13056 shinit = lookupvar("ENV");
13057 if (shinit != NULL && *shinit != '\0') {
13058 read_profile(shinit);
13059 }
13060 }
13061 state3:
13062 state = 4;
13063 if (minusc)
13064 evalstring(minusc, 0);
13065
13066 if (sflag || minusc == NULL) {
13067#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13068 if ( iflag ) {
13069 const char *hp = lookupvar("HISTFILE");
13070
13071 if (hp != NULL)
13072 line_input_state->hist_file = hp;
13073 }
13074#endif
13075 state4: /* XXX ??? - why isn't this before the "if" statement */
13076 cmdloop(1);
13077 }
13078#if PROFILE
13079 monitor(0);
13080#endif
13081#ifdef GPROF
13082 {
13083 extern void _mcleanup(void);
13084 _mcleanup();
13085 }
13086#endif
13087 exitshell();
13088 /* NOTREACHED */
13089}
13090
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000013091#if DEBUG
Denis Vlasenko8f8f2682006-10-03 21:00:43 +000013092const char *applet_name = "debug stuff usage";
Eric Andersenc470f442003-07-28 09:56:35 +000013093int main(int argc, char **argv)
13094{
13095 return ash_main(argc, argv);
13096}
13097#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013098
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013099
Eric Andersendf82f612001-06-28 07:46:40 +000013100/*-
13101 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013102 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013103 *
13104 * This code is derived from software contributed to Berkeley by
13105 * Kenneth Almquist.
13106 *
13107 * Redistribution and use in source and binary forms, with or without
13108 * modification, are permitted provided that the following conditions
13109 * are met:
13110 * 1. Redistributions of source code must retain the above copyright
13111 * notice, this list of conditions and the following disclaimer.
13112 * 2. Redistributions in binary form must reproduce the above copyright
13113 * notice, this list of conditions and the following disclaimer in the
13114 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013115 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013116 * may be used to endorse or promote products derived from this software
13117 * without specific prior written permission.
13118 *
13119 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13120 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13121 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13122 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13123 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13124 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13125 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13126 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13127 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13128 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13129 * SUCH DAMAGE.
13130 */