blob: a8383d805f3754d598535dc8b23a3e55be44510a [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 *
Eric Andersencb57d552001-06-28 07:25:16 +000011 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
Bernhard Reutner-Fischer86f5c992006-01-22 22:55:11 +000014 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Eric Andersendf82f612001-06-28 07:46:40 +000015 *
Eric Andersen81fe1232003-07-29 06:38:40 +000016 * Original BSD copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Eric Andersen90898442003-08-06 11:20:52 +000020 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
Eric Andersenef02f822004-03-11 13:34:24 +000023 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
Eric Andersen16767e22004-03-16 05:14:10 +000025 *
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000026 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
Eric Andersen16767e22004-03-16 05:14:10 +000027 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
Eric Andersen90898442003-08-06 11:20:52 +000030 */
31
Eric Andersen90898442003-08-06 11:20:52 +000032/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000033 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000034 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V.
36 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
37 * define DEBUG=2 to compile in and turn on debugging.
38 *
39 * When debugging is on, debugging info will be written to ./trace and
40 * a quit signal will generate a core dump.
41 */
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000042#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000043/* Tweak debug output verbosity here */
44#define DEBUG_TIME 0
45#define DEBUG_PID 1
46#define DEBUG_SIG 1
47
Eric Andersenc470f442003-07-28 09:56:35 +000048#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000049
50#define IFS_BROKEN
51
52#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000053
Denis Vlasenkob012b102007-02-19 22:43:01 +000054#if DEBUG
Denis Vlasenko653d8e72009-03-19 21:59:35 +000055# ifndef _GNU_SOURCE
56# define _GNU_SOURCE
57# endif
Denis Vlasenkoe26b2782008-02-12 07:40:29 +000058#endif
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000059
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000060#include "busybox.h" /* for applet_names */
Denis Vlasenko61befda2008-11-25 01:36:03 +000061//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
62//#include "applet_tables.h" doesn't work
Denis Vlasenkob012b102007-02-19 22:43:01 +000063#include <paths.h>
64#include <setjmp.h>
65#include <fnmatch.h>
Denis Vlasenko61befda2008-11-25 01:36:03 +000066
67#if defined SINGLE_APPLET_MAIN
68/* STANDALONE does not make sense, and won't compile */
69#undef CONFIG_FEATURE_SH_STANDALONE
70#undef ENABLE_FEATURE_SH_STANDALONE
71#undef USE_FEATURE_SH_STANDALONE
72#undef SKIP_FEATURE_SH_STANDALONE(...)
73#define ENABLE_FEATURE_SH_STANDALONE 0
74#define USE_FEATURE_SH_STANDALONE(...)
75#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000076#endif
77
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000078#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000079# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000080#endif
81
Denis Vlasenkob012b102007-02-19 22:43:01 +000082#if defined(__uClinux__)
Denis Vlasenko653d8e72009-03-19 21:59:35 +000083# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000084#endif
85
Denis Vlasenkob012b102007-02-19 22:43:01 +000086
Denis Vlasenko01631112007-12-16 17:20:38 +000087/* ============ Hash table sizes. Configurable. */
88
89#define VTABSIZE 39
90#define ATABSIZE 39
91#define CMDTABLESIZE 31 /* should be prime */
92
93
Denis Vlasenkob012b102007-02-19 22:43:01 +000094/* ============ Shell options */
95
96static const char *const optletters_optnames[] = {
97 "e" "errexit",
98 "f" "noglob",
99 "I" "ignoreeof",
100 "i" "interactive",
101 "m" "monitor",
102 "n" "noexec",
103 "s" "stdin",
104 "x" "xtrace",
105 "v" "verbose",
106 "C" "noclobber",
107 "a" "allexport",
108 "b" "notify",
109 "u" "nounset",
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000110 "\0" "vi"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000111#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000112 ,"\0" "nolog"
113 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000114#endif
115};
116
117#define optletters(n) optletters_optnames[(n)][0]
118#define optnames(n) (&optletters_optnames[(n)][1])
119
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000120enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000121
Eric Andersenc470f442003-07-28 09:56:35 +0000122
Denis Vlasenkob012b102007-02-19 22:43:01 +0000123/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000124
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000125static const char homestr[] ALIGN1 = "HOME";
126static const char snlfmt[] ALIGN1 = "%s\n";
127static const char illnum[] ALIGN1 = "Illegal number: %s";
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000128
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000129/*
Eric Andersenc470f442003-07-28 09:56:35 +0000130 * We enclose jmp_buf in a structure so that we can declare pointers to
131 * jump locations. The global variable handler contains the location to
132 * jump to when an exception occurs, and the global variable exception
Eric Andersenaff114c2004-04-14 17:51:38 +0000133 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000134 * exception handlers, the user should save the value of handler on entry
135 * to an inner scope, set handler to point to a jmploc structure for the
136 * inner scope, and restore handler on exit from the scope.
137 */
Eric Andersenc470f442003-07-28 09:56:35 +0000138struct jmploc {
139 jmp_buf loc;
140};
Denis Vlasenko01631112007-12-16 17:20:38 +0000141
142struct globals_misc {
143 /* pid of main shell */
144 int rootpid;
145 /* shell level: 0 for the main shell, 1 for its children, and so on */
146 int shlvl;
147#define rootshell (!shlvl)
148 char *minusc; /* argument to -c option */
149
150 char *curdir; // = nullstr; /* current working directory */
151 char *physdir; // = nullstr; /* physical working directory */
152
153 char *arg0; /* value of $0 */
154
155 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000156
157// disabled by vda: cannot understand how it was supposed to work -
158// cannot fix bugs. That's why you have to explain your non-trivial designs!
159// /* do we generate EXSIG events */
160// int exsig; /* counter */
161 volatile int suppressint; /* counter */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000162// TODO: rename
163// pendingsig -> pending_sig
164// intpending -> pending_int
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000165 volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
166 /* last pending signal */
167 volatile /*sig_atomic_t*/ smallint pendingsig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000168 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000169 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000170#define EXINT 0 /* SIGINT received */
171#define EXERROR 1 /* a generic error */
172#define EXSHELLPROC 2 /* execute a shell procedure */
173#define EXEXEC 3 /* command execution failed */
174#define EXEXIT 4 /* exit the shell */
175#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000176
Denis Vlasenko01631112007-12-16 17:20:38 +0000177 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000178 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000179
180 char optlist[NOPTS];
181#define eflag optlist[0]
182#define fflag optlist[1]
183#define Iflag optlist[2]
184#define iflag optlist[3]
185#define mflag optlist[4]
186#define nflag optlist[5]
187#define sflag optlist[6]
188#define xflag optlist[7]
189#define vflag optlist[8]
190#define Cflag optlist[9]
191#define aflag optlist[10]
192#define bflag optlist[11]
193#define uflag optlist[12]
194#define viflag optlist[13]
195#if DEBUG
196#define nolog optlist[14]
197#define debug optlist[15]
198#endif
199
200 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000201 /*
202 * Sigmode records the current value of the signal handlers for the various
203 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000204 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000205 */
206 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000207#define S_DFL 1 /* default signal handling (SIG_DFL) */
208#define S_CATCH 2 /* signal is caught */
209#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000210#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000211
Denis Vlasenko01631112007-12-16 17:20:38 +0000212 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000213 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000214 char *trap[NSIG];
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000215
216 /* Rarely referenced stuff */
217#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000218 /* Random number generators */
219 int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
220 uint32_t random_LCG; /* LCG (fast but weak) */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000221#endif
222 pid_t backgndpid; /* pid of last background process */
223 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000224};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000225extern struct globals_misc *const ash_ptr_to_globals_misc;
226#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000227#define rootpid (G_misc.rootpid )
228#define shlvl (G_misc.shlvl )
229#define minusc (G_misc.minusc )
230#define curdir (G_misc.curdir )
231#define physdir (G_misc.physdir )
232#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000233#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000234#define exception_type (G_misc.exception_type )
Denis Vlasenko01631112007-12-16 17:20:38 +0000235#define suppressint (G_misc.suppressint )
236#define intpending (G_misc.intpending )
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000237//#define exsig (G_misc.exsig )
Denis Vlasenko01631112007-12-16 17:20:38 +0000238#define pendingsig (G_misc.pendingsig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000239#define isloginsh (G_misc.isloginsh )
240#define nullstr (G_misc.nullstr )
241#define optlist (G_misc.optlist )
242#define sigmode (G_misc.sigmode )
243#define gotsig (G_misc.gotsig )
244#define trap (G_misc.trap )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000245#define random_galois_LFSR (G_misc.random_galois_LFSR)
246#define random_LCG (G_misc.random_LCG )
247#define backgndpid (G_misc.backgndpid )
248#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000249#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000250 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
251 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000252 curdir = nullstr; \
253 physdir = nullstr; \
254} while (0)
255
256
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000257/* ============ DEBUG */
258#if DEBUG
259static void trace_printf(const char *fmt, ...);
260static void trace_vprintf(const char *fmt, va_list va);
261# define TRACE(param) trace_printf param
262# define TRACEV(param) trace_vprintf param
263#else
264# define TRACE(param)
265# define TRACEV(param)
266#endif
267
268
Denis Vlasenko559691a2008-10-05 18:39:31 +0000269/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000270#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
271
272/* C99 say: "char" declaration may be signed or unsigned by default */
273#define signed_char2int(sc) ((int)(signed char)(sc))
274
Denis Vlasenko559691a2008-10-05 18:39:31 +0000275static int isdigit_str9(const char *str)
276{
277 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
278 while (--maxlen && isdigit(*str))
279 str++;
280 return (*str == '\0');
281}
Denis Vlasenko01631112007-12-16 17:20:38 +0000282
Denis Vlasenko559691a2008-10-05 18:39:31 +0000283
284/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000285/*
Eric Andersen2870d962001-07-02 17:27:21 +0000286 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000287 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000288 * much more efficient and portable. (But hacking the kernel is so much
289 * more fun than worrying about efficiency and portability. :-))
290 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000291#define INT_OFF do { \
292 suppressint++; \
293 xbarrier(); \
294} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000295
296/*
297 * Called to raise an exception. Since C doesn't include exceptions, we
298 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000299 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000300 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000301static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000302static void
303raise_exception(int e)
304{
305#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000306 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307 abort();
308#endif
309 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000310 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000311 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000312}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000313#if DEBUG
314#define raise_exception(e) do { \
315 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
316 raise_exception(e); \
317} while (0)
318#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000319
320/*
321 * Called from trap.c when a SIGINT is received. (If the user specifies
322 * that SIGINT is to be trapped or ignored using the trap builtin, then
323 * this routine is not called.) Suppressint is nonzero when interrupts
324 * are held using the INT_OFF macro. (The test for iflag is just
325 * defensive programming.)
326 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000327static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000328static void
329raise_interrupt(void)
330{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000331 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000332
333 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000334 /* Signal is not automatically unmasked after it is raised,
335 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000336 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000337 /* pendingsig = 0; - now done in onsig() */
338
Denis Vlasenko4b875702009-03-19 13:30:04 +0000339 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
341 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000342 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000343 signal(SIGINT, SIG_DFL);
344 raise(SIGINT);
345 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000346 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000347 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000348 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349 /* NOTREACHED */
350}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000351#if DEBUG
352#define raise_interrupt() do { \
353 TRACE(("raising interrupt on line %d\n", __LINE__)); \
354 raise_interrupt(); \
355} while (0)
356#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000357
358#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000359static void
360int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000361{
362 if (--suppressint == 0 && intpending) {
363 raise_interrupt();
364 }
365}
366#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000367static void
368force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000369{
370 suppressint = 0;
371 if (intpending)
372 raise_interrupt();
373}
374#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000375
376#else /* !ASH_OPTIMIZE_FOR_SIZE */
377
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000378#define INT_ON do { \
379 xbarrier(); \
380 if (--suppressint == 0 && intpending) \
381 raise_interrupt(); \
382} while (0)
383#define FORCE_INT_ON do { \
384 xbarrier(); \
385 suppressint = 0; \
386 if (intpending) \
387 raise_interrupt(); \
388} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000389#endif /* !ASH_OPTIMIZE_FOR_SIZE */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000390
391#define SAVE_INT(v) ((v) = suppressint)
392
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000393#define RESTORE_INT(v) do { \
394 xbarrier(); \
395 suppressint = (v); \
396 if (suppressint == 0 && intpending) \
397 raise_interrupt(); \
398} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000399
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000400
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000401/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000402
Eric Andersenc470f442003-07-28 09:56:35 +0000403static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000404outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000405{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000406 INT_OFF;
407 fputs(p, file);
408 INT_ON;
409}
410
411static void
412flush_stdout_stderr(void)
413{
414 INT_OFF;
415 fflush(stdout);
416 fflush(stderr);
417 INT_ON;
418}
419
420static void
421flush_stderr(void)
422{
423 INT_OFF;
424 fflush(stderr);
425 INT_ON;
426}
427
428static void
429outcslow(int c, FILE *dest)
430{
431 INT_OFF;
432 putc(c, dest);
433 fflush(dest);
434 INT_ON;
435}
436
437static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
438static int
439out1fmt(const char *fmt, ...)
440{
441 va_list ap;
442 int r;
443
444 INT_OFF;
445 va_start(ap, fmt);
446 r = vprintf(fmt, ap);
447 va_end(ap);
448 INT_ON;
449 return r;
450}
451
452static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
453static int
454fmtstr(char *outbuf, size_t length, const char *fmt, ...)
455{
456 va_list ap;
457 int ret;
458
459 va_start(ap, fmt);
460 INT_OFF;
461 ret = vsnprintf(outbuf, length, fmt, ap);
462 va_end(ap);
463 INT_ON;
464 return ret;
465}
466
467static void
468out1str(const char *p)
469{
470 outstr(p, stdout);
471}
472
473static void
474out2str(const char *p)
475{
476 outstr(p, stderr);
477 flush_stderr();
478}
479
480
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000481/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000482
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000483/* control characters in argument strings */
484#define CTLESC '\201' /* escape next character */
485#define CTLVAR '\202' /* variable defn */
486#define CTLENDVAR '\203'
487#define CTLBACKQ '\204'
488#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
489/* CTLBACKQ | CTLQUOTE == '\205' */
490#define CTLARI '\206' /* arithmetic expression */
491#define CTLENDARI '\207'
492#define CTLQUOTEMARK '\210'
493
494/* variable substitution byte (follows CTLVAR) */
495#define VSTYPE 0x0f /* type of variable substitution */
496#define VSNUL 0x10 /* colon--treat the empty string as unset */
497#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
498
499/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000500#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
501#define VSMINUS 0x2 /* ${var-text} */
502#define VSPLUS 0x3 /* ${var+text} */
503#define VSQUESTION 0x4 /* ${var?message} */
504#define VSASSIGN 0x5 /* ${var=text} */
505#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
506#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
507#define VSTRIMLEFT 0x8 /* ${var#pattern} */
508#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
509#define VSLENGTH 0xa /* ${#var} */
510#if ENABLE_ASH_BASH_COMPAT
511#define VSSUBSTR 0xc /* ${var:position:length} */
512#define VSREPLACE 0xd /* ${var/pattern/replacement} */
513#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
514#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000515
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000516static const char dolatstr[] ALIGN1 = {
517 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
518};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000519
Denis Vlasenko559691a2008-10-05 18:39:31 +0000520#define NCMD 0
521#define NPIPE 1
522#define NREDIR 2
523#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000524#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000525#define NAND 5
526#define NOR 6
527#define NSEMI 7
528#define NIF 8
529#define NWHILE 9
530#define NUNTIL 10
531#define NFOR 11
532#define NCASE 12
533#define NCLIST 13
534#define NDEFUN 14
535#define NARG 15
536#define NTO 16
537#if ENABLE_ASH_BASH_COMPAT
538#define NTO2 17
539#endif
540#define NCLOBBER 18
541#define NFROM 19
542#define NFROMTO 20
543#define NAPPEND 21
544#define NTOFD 22
545#define NFROMFD 23
546#define NHERE 24
547#define NXHERE 25
548#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000549#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000550
551union node;
552
553struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000554 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000555 union node *assign;
556 union node *args;
557 union node *redirect;
558};
559
560struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000561 smallint type;
562 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000563 struct nodelist *cmdlist;
564};
565
566struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000567 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000568 union node *n;
569 union node *redirect;
570};
571
572struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000573 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000574 union node *ch1;
575 union node *ch2;
576};
577
578struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000579 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000580 union node *test;
581 union node *ifpart;
582 union node *elsepart;
583};
584
585struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000586 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000587 union node *args;
588 union node *body;
589 char *var;
590};
591
592struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000593 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000594 union node *expr;
595 union node *cases;
596};
597
598struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000599 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000600 union node *next;
601 union node *pattern;
602 union node *body;
603};
604
605struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000606 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000607 union node *next;
608 char *text;
609 struct nodelist *backquote;
610};
611
Denis Vlasenko559691a2008-10-05 18:39:31 +0000612/* nfile and ndup layout must match!
613 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
614 * that it is actually NTO2 (>&file), and change its type.
615 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000616struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000617 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000618 union node *next;
619 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621 union node *fname;
622 char *expfname;
623};
624
625struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000626 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627 union node *next;
628 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000629 int dupfd;
630 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000631 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000632};
633
634struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000635 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000636 union node *next;
637 int fd;
638 union node *doc;
639};
640
641struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000642 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000643 union node *com;
644};
645
646union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000647 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000648 struct ncmd ncmd;
649 struct npipe npipe;
650 struct nredir nredir;
651 struct nbinary nbinary;
652 struct nif nif;
653 struct nfor nfor;
654 struct ncase ncase;
655 struct nclist nclist;
656 struct narg narg;
657 struct nfile nfile;
658 struct ndup ndup;
659 struct nhere nhere;
660 struct nnot nnot;
661};
662
663struct nodelist {
664 struct nodelist *next;
665 union node *n;
666};
667
668struct funcnode {
669 int count;
670 union node n;
671};
672
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000673/*
674 * Free a parse tree.
675 */
676static void
677freefunc(struct funcnode *f)
678{
679 if (f && --f->count < 0)
680 free(f);
681}
682
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000683
684/* ============ Debugging output */
685
686#if DEBUG
687
688static FILE *tracefile;
689
690static void
691trace_printf(const char *fmt, ...)
692{
693 va_list va;
694
695 if (debug != 1)
696 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000697 if (DEBUG_TIME)
698 fprintf(tracefile, "%u ", (int) time(NULL));
699 if (DEBUG_PID)
700 fprintf(tracefile, "[%u] ", (int) getpid());
701 if (DEBUG_SIG)
702 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000703 va_start(va, fmt);
704 vfprintf(tracefile, fmt, va);
705 va_end(va);
706}
707
708static void
709trace_vprintf(const char *fmt, va_list va)
710{
711 if (debug != 1)
712 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000713 if (DEBUG_TIME)
714 fprintf(tracefile, "%u ", (int) time(NULL));
715 if (DEBUG_PID)
716 fprintf(tracefile, "[%u] ", (int) getpid());
717 if (DEBUG_SIG)
718 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000719 vfprintf(tracefile, fmt, va);
720}
721
722static void
723trace_puts(const char *s)
724{
725 if (debug != 1)
726 return;
727 fputs(s, tracefile);
728}
729
730static void
731trace_puts_quoted(char *s)
732{
733 char *p;
734 char c;
735
736 if (debug != 1)
737 return;
738 putc('"', tracefile);
739 for (p = s; *p; p++) {
740 switch (*p) {
741 case '\n': c = 'n'; goto backslash;
742 case '\t': c = 't'; goto backslash;
743 case '\r': c = 'r'; goto backslash;
744 case '"': c = '"'; goto backslash;
745 case '\\': c = '\\'; goto backslash;
746 case CTLESC: c = 'e'; goto backslash;
747 case CTLVAR: c = 'v'; goto backslash;
748 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
749 case CTLBACKQ: c = 'q'; goto backslash;
750 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
751 backslash:
752 putc('\\', tracefile);
753 putc(c, tracefile);
754 break;
755 default:
756 if (*p >= ' ' && *p <= '~')
757 putc(*p, tracefile);
758 else {
759 putc('\\', tracefile);
760 putc(*p >> 6 & 03, tracefile);
761 putc(*p >> 3 & 07, tracefile);
762 putc(*p & 07, tracefile);
763 }
764 break;
765 }
766 }
767 putc('"', tracefile);
768}
769
770static void
771trace_puts_args(char **ap)
772{
773 if (debug != 1)
774 return;
775 if (!*ap)
776 return;
777 while (1) {
778 trace_puts_quoted(*ap);
779 if (!*++ap) {
780 putc('\n', tracefile);
781 break;
782 }
783 putc(' ', tracefile);
784 }
785}
786
787static void
788opentrace(void)
789{
790 char s[100];
791#ifdef O_APPEND
792 int flags;
793#endif
794
795 if (debug != 1) {
796 if (tracefile)
797 fflush(tracefile);
798 /* leave open because libedit might be using it */
799 return;
800 }
801 strcpy(s, "./trace");
802 if (tracefile) {
803 if (!freopen(s, "a", tracefile)) {
804 fprintf(stderr, "Can't re-open %s\n", s);
805 debug = 0;
806 return;
807 }
808 } else {
809 tracefile = fopen(s, "a");
810 if (tracefile == NULL) {
811 fprintf(stderr, "Can't open %s\n", s);
812 debug = 0;
813 return;
814 }
815 }
816#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000817 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000818 if (flags >= 0)
819 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
820#endif
821 setlinebuf(tracefile);
822 fputs("\nTracing started.\n", tracefile);
823}
824
825static void
826indent(int amount, char *pfx, FILE *fp)
827{
828 int i;
829
830 for (i = 0; i < amount; i++) {
831 if (pfx && i == amount - 1)
832 fputs(pfx, fp);
833 putc('\t', fp);
834 }
835}
836
837/* little circular references here... */
838static void shtree(union node *n, int ind, char *pfx, FILE *fp);
839
840static void
841sharg(union node *arg, FILE *fp)
842{
843 char *p;
844 struct nodelist *bqlist;
845 int subtype;
846
847 if (arg->type != NARG) {
848 out1fmt("<node type %d>\n", arg->type);
849 abort();
850 }
851 bqlist = arg->narg.backquote;
852 for (p = arg->narg.text; *p; p++) {
853 switch (*p) {
854 case CTLESC:
855 putc(*++p, fp);
856 break;
857 case CTLVAR:
858 putc('$', fp);
859 putc('{', fp);
860 subtype = *++p;
861 if (subtype == VSLENGTH)
862 putc('#', fp);
863
864 while (*p != '=')
865 putc(*p++, fp);
866
867 if (subtype & VSNUL)
868 putc(':', fp);
869
870 switch (subtype & VSTYPE) {
871 case VSNORMAL:
872 putc('}', fp);
873 break;
874 case VSMINUS:
875 putc('-', fp);
876 break;
877 case VSPLUS:
878 putc('+', fp);
879 break;
880 case VSQUESTION:
881 putc('?', fp);
882 break;
883 case VSASSIGN:
884 putc('=', fp);
885 break;
886 case VSTRIMLEFT:
887 putc('#', fp);
888 break;
889 case VSTRIMLEFTMAX:
890 putc('#', fp);
891 putc('#', fp);
892 break;
893 case VSTRIMRIGHT:
894 putc('%', fp);
895 break;
896 case VSTRIMRIGHTMAX:
897 putc('%', fp);
898 putc('%', fp);
899 break;
900 case VSLENGTH:
901 break;
902 default:
903 out1fmt("<subtype %d>", subtype);
904 }
905 break;
906 case CTLENDVAR:
907 putc('}', fp);
908 break;
909 case CTLBACKQ:
910 case CTLBACKQ|CTLQUOTE:
911 putc('$', fp);
912 putc('(', fp);
913 shtree(bqlist->n, -1, NULL, fp);
914 putc(')', fp);
915 break;
916 default:
917 putc(*p, fp);
918 break;
919 }
920 }
921}
922
923static void
924shcmd(union node *cmd, FILE *fp)
925{
926 union node *np;
927 int first;
928 const char *s;
929 int dftfd;
930
931 first = 1;
932 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000933 if (!first)
934 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000935 sharg(np, fp);
936 first = 0;
937 }
938 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000939 if (!first)
940 putc(' ', fp);
941 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000942 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000943 case NTO: s = ">>"+1; dftfd = 1; break;
944 case NCLOBBER: s = ">|"; dftfd = 1; break;
945 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000946#if ENABLE_ASH_BASH_COMPAT
947 case NTO2:
948#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000949 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000950 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000951 case NFROMFD: s = "<&"; break;
952 case NFROMTO: s = "<>"; break;
953 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954 }
955 if (np->nfile.fd != dftfd)
956 fprintf(fp, "%d", np->nfile.fd);
957 fputs(s, fp);
958 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
959 fprintf(fp, "%d", np->ndup.dupfd);
960 } else {
961 sharg(np->nfile.fname, fp);
962 }
963 first = 0;
964 }
965}
966
967static void
968shtree(union node *n, int ind, char *pfx, FILE *fp)
969{
970 struct nodelist *lp;
971 const char *s;
972
973 if (n == NULL)
974 return;
975
976 indent(ind, pfx, fp);
977 switch (n->type) {
978 case NSEMI:
979 s = "; ";
980 goto binop;
981 case NAND:
982 s = " && ";
983 goto binop;
984 case NOR:
985 s = " || ";
986 binop:
987 shtree(n->nbinary.ch1, ind, NULL, fp);
988 /* if (ind < 0) */
989 fputs(s, fp);
990 shtree(n->nbinary.ch2, ind, NULL, fp);
991 break;
992 case NCMD:
993 shcmd(n, fp);
994 if (ind >= 0)
995 putc('\n', fp);
996 break;
997 case NPIPE:
998 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
999 shcmd(lp->n, fp);
1000 if (lp->next)
1001 fputs(" | ", fp);
1002 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001003 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001004 fputs(" &", fp);
1005 if (ind >= 0)
1006 putc('\n', fp);
1007 break;
1008 default:
1009 fprintf(fp, "<node type %d>", n->type);
1010 if (ind >= 0)
1011 putc('\n', fp);
1012 break;
1013 }
1014}
1015
1016static void
1017showtree(union node *n)
1018{
1019 trace_puts("showtree called\n");
1020 shtree(n, 1, NULL, stdout);
1021}
1022
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001023#endif /* DEBUG */
1024
1025
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001026/* ============ Parser data */
1027
1028/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001029 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1030 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001031struct strlist {
1032 struct strlist *next;
1033 char *text;
1034};
1035
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001036struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001037
Denis Vlasenkob012b102007-02-19 22:43:01 +00001038struct strpush {
1039 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001040 char *prev_string;
1041 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001042#if ENABLE_ASH_ALIAS
1043 struct alias *ap; /* if push was associated with an alias */
1044#endif
1045 char *string; /* remember the string since it may change */
1046};
1047
1048struct parsefile {
1049 struct parsefile *prev; /* preceding file on stack */
1050 int linno; /* current line */
1051 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001052 int left_in_line; /* number of chars left in this line */
1053 int left_in_buffer; /* number of chars left in this buffer past the line */
1054 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001055 char *buf; /* input buffer */
1056 struct strpush *strpush; /* for pushing strings at this level */
1057 struct strpush basestrpush; /* so pushing one is fast */
1058};
1059
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001060static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001061static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001062static int startlinno; /* line # where last token started */
1063static char *commandname; /* currently executing command */
1064static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001065static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001066
1067
1068/* ============ Message printing */
1069
1070static void
1071ash_vmsg(const char *msg, va_list ap)
1072{
1073 fprintf(stderr, "%s: ", arg0);
1074 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001075 if (strcmp(arg0, commandname))
1076 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001077 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001078 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001079 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001080 vfprintf(stderr, msg, ap);
1081 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001082}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001083
1084/*
1085 * Exverror is called to raise the error exception. If the second argument
1086 * is not NULL then error prints an error message using printf style
1087 * formatting. It then raises the error exception.
1088 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001089static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001090static void
1091ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001092{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001093#if DEBUG
1094 if (msg) {
1095 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1096 TRACEV((msg, ap));
1097 TRACE(("\") pid=%d\n", getpid()));
1098 } else
1099 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1100 if (msg)
1101#endif
1102 ash_vmsg(msg, ap);
1103
1104 flush_stdout_stderr();
1105 raise_exception(cond);
1106 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001107}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001108
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001109static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001110static void
1111ash_msg_and_raise_error(const char *msg, ...)
1112{
1113 va_list ap;
1114
1115 va_start(ap, msg);
1116 ash_vmsg_and_raise(EXERROR, msg, ap);
1117 /* NOTREACHED */
1118 va_end(ap);
1119}
1120
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001121static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001122static void
1123ash_msg_and_raise(int cond, const char *msg, ...)
1124{
1125 va_list ap;
1126
1127 va_start(ap, msg);
1128 ash_vmsg_and_raise(cond, msg, ap);
1129 /* NOTREACHED */
1130 va_end(ap);
1131}
1132
1133/*
1134 * error/warning routines for external builtins
1135 */
1136static void
1137ash_msg(const char *fmt, ...)
1138{
1139 va_list ap;
1140
1141 va_start(ap, fmt);
1142 ash_vmsg(fmt, ap);
1143 va_end(ap);
1144}
1145
1146/*
1147 * Return a string describing an error. The returned string may be a
1148 * pointer to a static buffer that will be overwritten on the next call.
1149 * Action describes the operation that got the error.
1150 */
1151static const char *
1152errmsg(int e, const char *em)
1153{
1154 if (e == ENOENT || e == ENOTDIR) {
1155 return em;
1156 }
1157 return strerror(e);
1158}
1159
1160
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001161/* ============ Memory allocation */
1162
1163/*
1164 * It appears that grabstackstr() will barf with such alignments
1165 * because stalloc() will return a string allocated in a new stackblock.
1166 */
1167#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1168enum {
1169 /* Most machines require the value returned from malloc to be aligned
1170 * in some way. The following macro will get this right
1171 * on many machines. */
1172 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1173 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001174 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001175};
1176
1177struct stack_block {
1178 struct stack_block *prev;
1179 char space[MINSIZE];
1180};
1181
1182struct stackmark {
1183 struct stack_block *stackp;
1184 char *stacknxt;
1185 size_t stacknleft;
1186 struct stackmark *marknext;
1187};
1188
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001189
Denis Vlasenko01631112007-12-16 17:20:38 +00001190struct globals_memstack {
1191 struct stack_block *g_stackp; // = &stackbase;
1192 struct stackmark *markp;
1193 char *g_stacknxt; // = stackbase.space;
1194 char *sstrend; // = stackbase.space + MINSIZE;
1195 size_t g_stacknleft; // = MINSIZE;
1196 int herefd; // = -1;
1197 struct stack_block stackbase;
1198};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001199extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1200#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001201#define g_stackp (G_memstack.g_stackp )
1202#define markp (G_memstack.markp )
1203#define g_stacknxt (G_memstack.g_stacknxt )
1204#define sstrend (G_memstack.sstrend )
1205#define g_stacknleft (G_memstack.g_stacknleft)
1206#define herefd (G_memstack.herefd )
1207#define stackbase (G_memstack.stackbase )
1208#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001209 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1210 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001211 g_stackp = &stackbase; \
1212 g_stacknxt = stackbase.space; \
1213 g_stacknleft = MINSIZE; \
1214 sstrend = stackbase.space + MINSIZE; \
1215 herefd = -1; \
1216} while (0)
1217
1218#define stackblock() ((void *)g_stacknxt)
1219#define stackblocksize() g_stacknleft
1220
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001221
1222static void *
1223ckrealloc(void * p, size_t nbytes)
1224{
1225 p = realloc(p, nbytes);
1226 if (!p)
1227 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1228 return p;
1229}
1230
1231static void *
1232ckmalloc(size_t nbytes)
1233{
1234 return ckrealloc(NULL, nbytes);
1235}
1236
Denis Vlasenko597906c2008-02-20 16:38:54 +00001237static void *
1238ckzalloc(size_t nbytes)
1239{
1240 return memset(ckmalloc(nbytes), 0, nbytes);
1241}
1242
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001243/*
1244 * Make a copy of a string in safe storage.
1245 */
1246static char *
1247ckstrdup(const char *s)
1248{
1249 char *p = strdup(s);
1250 if (!p)
1251 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1252 return p;
1253}
1254
1255/*
1256 * Parse trees for commands are allocated in lifo order, so we use a stack
1257 * to make this more efficient, and also to avoid all sorts of exception
1258 * handling code to handle interrupts in the middle of a parse.
1259 *
1260 * The size 504 was chosen because the Ultrix malloc handles that size
1261 * well.
1262 */
1263static void *
1264stalloc(size_t nbytes)
1265{
1266 char *p;
1267 size_t aligned;
1268
1269 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001270 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001271 size_t len;
1272 size_t blocksize;
1273 struct stack_block *sp;
1274
1275 blocksize = aligned;
1276 if (blocksize < MINSIZE)
1277 blocksize = MINSIZE;
1278 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1279 if (len < blocksize)
1280 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1281 INT_OFF;
1282 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001283 sp->prev = g_stackp;
1284 g_stacknxt = sp->space;
1285 g_stacknleft = blocksize;
1286 sstrend = g_stacknxt + blocksize;
1287 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001288 INT_ON;
1289 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001290 p = g_stacknxt;
1291 g_stacknxt += aligned;
1292 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001293 return p;
1294}
1295
Denis Vlasenko597906c2008-02-20 16:38:54 +00001296static void *
1297stzalloc(size_t nbytes)
1298{
1299 return memset(stalloc(nbytes), 0, nbytes);
1300}
1301
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001302static void
1303stunalloc(void *p)
1304{
1305#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001306 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001307 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001308 abort();
1309 }
1310#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001311 g_stacknleft += g_stacknxt - (char *)p;
1312 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001313}
1314
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001315/*
1316 * Like strdup but works with the ash stack.
1317 */
1318static char *
1319ststrdup(const char *p)
1320{
1321 size_t len = strlen(p) + 1;
1322 return memcpy(stalloc(len), p, len);
1323}
1324
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001325static void
1326setstackmark(struct stackmark *mark)
1327{
Denis Vlasenko01631112007-12-16 17:20:38 +00001328 mark->stackp = g_stackp;
1329 mark->stacknxt = g_stacknxt;
1330 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331 mark->marknext = markp;
1332 markp = mark;
1333}
1334
1335static void
1336popstackmark(struct stackmark *mark)
1337{
1338 struct stack_block *sp;
1339
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001340 if (!mark->stackp)
1341 return;
1342
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343 INT_OFF;
1344 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001345 while (g_stackp != mark->stackp) {
1346 sp = g_stackp;
1347 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348 free(sp);
1349 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001350 g_stacknxt = mark->stacknxt;
1351 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001352 sstrend = mark->stacknxt + mark->stacknleft;
1353 INT_ON;
1354}
1355
1356/*
1357 * When the parser reads in a string, it wants to stick the string on the
1358 * stack and only adjust the stack pointer when it knows how big the
1359 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1360 * of space on top of the stack and stackblocklen returns the length of
1361 * this block. Growstackblock will grow this space by at least one byte,
1362 * possibly moving it (like realloc). Grabstackblock actually allocates the
1363 * part of the block that has been used.
1364 */
1365static void
1366growstackblock(void)
1367{
1368 size_t newlen;
1369
Denis Vlasenko01631112007-12-16 17:20:38 +00001370 newlen = g_stacknleft * 2;
1371 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001372 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1373 if (newlen < 128)
1374 newlen += 128;
1375
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001377 struct stack_block *oldstackp;
1378 struct stackmark *xmark;
1379 struct stack_block *sp;
1380 struct stack_block *prevstackp;
1381 size_t grosslen;
1382
1383 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001384 oldstackp = g_stackp;
1385 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001386 prevstackp = sp->prev;
1387 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1388 sp = ckrealloc(sp, grosslen);
1389 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001390 g_stackp = sp;
1391 g_stacknxt = sp->space;
1392 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001393 sstrend = sp->space + newlen;
1394
1395 /*
1396 * Stack marks pointing to the start of the old block
1397 * must be relocated to point to the new block
1398 */
1399 xmark = markp;
1400 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001401 xmark->stackp = g_stackp;
1402 xmark->stacknxt = g_stacknxt;
1403 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001404 xmark = xmark->marknext;
1405 }
1406 INT_ON;
1407 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001408 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001409 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001410 char *p = stalloc(newlen);
1411
1412 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001413 g_stacknxt = memcpy(p, oldspace, oldlen);
1414 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001415 }
1416}
1417
1418static void
1419grabstackblock(size_t len)
1420{
1421 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001422 g_stacknxt += len;
1423 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001424}
1425
1426/*
1427 * The following routines are somewhat easier to use than the above.
1428 * The user declares a variable of type STACKSTR, which may be declared
1429 * to be a register. The macro STARTSTACKSTR initializes things. Then
1430 * the user uses the macro STPUTC to add characters to the string. In
1431 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1432 * grown as necessary. When the user is done, she can just leave the
1433 * string there and refer to it using stackblock(). Or she can allocate
1434 * the space for it using grabstackstr(). If it is necessary to allow
1435 * someone else to use the stack temporarily and then continue to grow
1436 * the string, the user should use grabstack to allocate the space, and
1437 * then call ungrabstr(p) to return to the previous mode of operation.
1438 *
1439 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1440 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1441 * is space for at least one character.
1442 */
1443static void *
1444growstackstr(void)
1445{
1446 size_t len = stackblocksize();
1447 if (herefd >= 0 && len >= 1024) {
1448 full_write(herefd, stackblock(), len);
1449 return stackblock();
1450 }
1451 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001452 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001453}
1454
1455/*
1456 * Called from CHECKSTRSPACE.
1457 */
1458static char *
1459makestrspace(size_t newlen, char *p)
1460{
Denis Vlasenko01631112007-12-16 17:20:38 +00001461 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001462 size_t size = stackblocksize();
1463
1464 for (;;) {
1465 size_t nleft;
1466
1467 size = stackblocksize();
1468 nleft = size - len;
1469 if (nleft >= newlen)
1470 break;
1471 growstackblock();
1472 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001473 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001474}
1475
1476static char *
1477stack_nputstr(const char *s, size_t n, char *p)
1478{
1479 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001480 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001481 return p;
1482}
1483
1484static char *
1485stack_putstr(const char *s, char *p)
1486{
1487 return stack_nputstr(s, strlen(s), p);
1488}
1489
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001490static char *
1491_STPUTC(int c, char *p)
1492{
1493 if (p == sstrend)
1494 p = growstackstr();
1495 *p++ = c;
1496 return p;
1497}
1498
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001499#define STARTSTACKSTR(p) ((p) = stackblock())
1500#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001501#define CHECKSTRSPACE(n, p) do { \
1502 char *q = (p); \
1503 size_t l = (n); \
1504 size_t m = sstrend - q; \
1505 if (l > m) \
1506 (p) = makestrspace(l, q); \
1507} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001508#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001509#define STACKSTRNUL(p) do { \
1510 if ((p) == sstrend) \
1511 (p) = growstackstr(); \
1512 *(p) = '\0'; \
1513} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001514#define STUNPUTC(p) (--(p))
1515#define STTOPC(p) ((p)[-1])
1516#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001517
1518#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001519#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001520#define stackstrend() ((void *)sstrend)
1521
1522
1523/* ============ String helpers */
1524
1525/*
1526 * prefix -- see if pfx is a prefix of string.
1527 */
1528static char *
1529prefix(const char *string, const char *pfx)
1530{
1531 while (*pfx) {
1532 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001533 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001534 }
1535 return (char *) string;
1536}
1537
1538/*
1539 * Check for a valid number. This should be elsewhere.
1540 */
1541static int
1542is_number(const char *p)
1543{
1544 do {
1545 if (!isdigit(*p))
1546 return 0;
1547 } while (*++p != '\0');
1548 return 1;
1549}
1550
1551/*
1552 * Convert a string of digits to an integer, printing an error message on
1553 * failure.
1554 */
1555static int
1556number(const char *s)
1557{
1558 if (!is_number(s))
1559 ash_msg_and_raise_error(illnum, s);
1560 return atoi(s);
1561}
1562
1563/*
1564 * Produce a possibly single quoted string suitable as input to the shell.
1565 * The return string is allocated on the stack.
1566 */
1567static char *
1568single_quote(const char *s)
1569{
1570 char *p;
1571
1572 STARTSTACKSTR(p);
1573
1574 do {
1575 char *q;
1576 size_t len;
1577
1578 len = strchrnul(s, '\'') - s;
1579
1580 q = p = makestrspace(len + 3, p);
1581
1582 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001583 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001584 *q++ = '\'';
1585 s += len;
1586
1587 STADJUST(q - p, p);
1588
1589 len = strspn(s, "'");
1590 if (!len)
1591 break;
1592
1593 q = p = makestrspace(len + 3, p);
1594
1595 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001596 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001597 *q++ = '"';
1598 s += len;
1599
1600 STADJUST(q - p, p);
1601 } while (*s);
1602
1603 USTPUTC(0, p);
1604
1605 return stackblock();
1606}
1607
1608
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001609/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001610
1611static char **argptr; /* argument list for builtin commands */
1612static char *optionarg; /* set by nextopt (like getopt) */
1613static char *optptr; /* used by nextopt */
1614
1615/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001616 * XXX - should get rid of. Have all builtins use getopt(3).
1617 * The library getopt must have the BSD extension static variable
1618 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001619 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001620 * Standard option processing (a la getopt) for builtin routines.
1621 * The only argument that is passed to nextopt is the option string;
1622 * the other arguments are unnecessary. It returns the character,
1623 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001624 */
1625static int
1626nextopt(const char *optstring)
1627{
1628 char *p;
1629 const char *q;
1630 char c;
1631
1632 p = optptr;
1633 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001634 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001635 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001636 if (p == NULL)
1637 return '\0';
1638 if (*p != '-')
1639 return '\0';
1640 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001641 return '\0';
1642 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001643 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001644 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001645 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001646 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001647 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001648 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001649 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001651 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001652 if (*++q == ':')
1653 q++;
1654 }
1655 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001656 if (*p == '\0') {
1657 p = *argptr++;
1658 if (p == NULL)
1659 ash_msg_and_raise_error("no arg for -%c option", c);
1660 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001661 optionarg = p;
1662 p = NULL;
1663 }
1664 optptr = p;
1665 return c;
1666}
1667
1668
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001669/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001670
Denis Vlasenko01631112007-12-16 17:20:38 +00001671/*
1672 * The parsefile structure pointed to by the global variable parsefile
1673 * contains information about the current file being read.
1674 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001675struct shparam {
1676 int nparam; /* # of positional parameters (without $0) */
1677#if ENABLE_ASH_GETOPTS
1678 int optind; /* next parameter to be processed by getopts */
1679 int optoff; /* used by getopts */
1680#endif
1681 unsigned char malloced; /* if parameter list dynamically allocated */
1682 char **p; /* parameter list */
1683};
1684
1685/*
1686 * Free the list of positional parameters.
1687 */
1688static void
1689freeparam(volatile struct shparam *param)
1690{
Denis Vlasenko01631112007-12-16 17:20:38 +00001691 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001692 char **ap, **ap1;
1693 ap = ap1 = param->p;
1694 while (*ap)
1695 free(*ap++);
1696 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001697 }
1698}
1699
1700#if ENABLE_ASH_GETOPTS
1701static void getoptsreset(const char *value);
1702#endif
1703
1704struct var {
1705 struct var *next; /* next entry in hash list */
1706 int flags; /* flags are defined above */
1707 const char *text; /* name=value */
1708 void (*func)(const char *); /* function to be called when */
1709 /* the variable gets set/unset */
1710};
1711
1712struct localvar {
1713 struct localvar *next; /* next local variable in list */
1714 struct var *vp; /* the variable that was made local */
1715 int flags; /* saved flags */
1716 const char *text; /* saved text */
1717};
1718
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001719/* flags */
1720#define VEXPORT 0x01 /* variable is exported */
1721#define VREADONLY 0x02 /* variable cannot be modified */
1722#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1723#define VTEXTFIXED 0x08 /* text is statically allocated */
1724#define VSTACK 0x10 /* text is allocated on the stack */
1725#define VUNSET 0x20 /* the variable is not set */
1726#define VNOFUNC 0x40 /* don't call the callback function */
1727#define VNOSET 0x80 /* do not set variable - just readonly test */
1728#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001729#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001730# define VDYNAMIC 0x200 /* dynamic variable */
1731#else
1732# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733#endif
1734
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001736static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737#define defifs (defifsvar + 4)
1738#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001739static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740#endif
1741
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001742
Denis Vlasenko01631112007-12-16 17:20:38 +00001743/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001744#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001745static void
1746change_lc_all(const char *value)
1747{
1748 if (value && *value != '\0')
1749 setlocale(LC_ALL, value);
1750}
1751static void
1752change_lc_ctype(const char *value)
1753{
1754 if (value && *value != '\0')
1755 setlocale(LC_CTYPE, value);
1756}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001757#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758#if ENABLE_ASH_MAIL
1759static void chkmail(void);
1760static void changemail(const char *);
1761#endif
1762static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001763#if ENABLE_ASH_RANDOM_SUPPORT
1764static void change_random(const char *);
1765#endif
1766
Denis Vlasenko01631112007-12-16 17:20:38 +00001767static const struct {
1768 int flags;
1769 const char *text;
1770 void (*func)(const char *);
1771} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001772#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001773 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001775 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001778 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1779 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001781 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1782 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1783 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1784 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001786 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001787#endif
1788#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001789 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790#endif
1791#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001792 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1793 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001794#endif
1795#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001796 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797#endif
1798};
1799
Denis Vlasenko0b769642008-07-24 07:54:57 +00001800struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001801
1802struct globals_var {
1803 struct shparam shellparam; /* $@ current positional parameters */
1804 struct redirtab *redirlist;
1805 int g_nullredirs;
1806 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1807 struct var *vartab[VTABSIZE];
1808 struct var varinit[ARRAY_SIZE(varinit_data)];
1809};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001810extern struct globals_var *const ash_ptr_to_globals_var;
1811#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001812#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001813//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001814#define g_nullredirs (G_var.g_nullredirs )
1815#define preverrout_fd (G_var.preverrout_fd)
1816#define vartab (G_var.vartab )
1817#define varinit (G_var.varinit )
1818#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001819 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001820 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1821 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001822 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1823 varinit[i].flags = varinit_data[i].flags; \
1824 varinit[i].text = varinit_data[i].text; \
1825 varinit[i].func = varinit_data[i].func; \
1826 } \
1827} while (0)
1828
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001829#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001830#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001831# define vmail (&vifs)[1]
1832# define vmpath (&vmail)[1]
1833# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001834#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001835# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001837#define vps1 (&vpath)[1]
1838#define vps2 (&vps1)[1]
1839#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001841# define voptind (&vps4)[1]
1842# if ENABLE_ASH_RANDOM_SUPPORT
1843# define vrandom (&voptind)[1]
1844# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001845#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001846# if ENABLE_ASH_RANDOM_SUPPORT
1847# define vrandom (&vps4)[1]
1848# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001849#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001850
1851/*
1852 * The following macros access the values of the above variables.
1853 * They have to skip over the name. They return the null string
1854 * for unset variables.
1855 */
1856#define ifsval() (vifs.text + 4)
1857#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001858#if ENABLE_ASH_MAIL
1859# define mailval() (vmail.text + 5)
1860# define mpathval() (vmpath.text + 9)
1861# define mpathset() ((vmpath.flags & VUNSET) == 0)
1862#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001863#define pathval() (vpath.text + 5)
1864#define ps1val() (vps1.text + 4)
1865#define ps2val() (vps2.text + 4)
1866#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001867#if ENABLE_ASH_GETOPTS
1868# define optindval() (voptind.text + 7)
1869#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001870
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001871
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001872#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1873#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1874
Denis Vlasenko01631112007-12-16 17:20:38 +00001875#if ENABLE_ASH_GETOPTS
1876static void
1877getoptsreset(const char *value)
1878{
1879 shellparam.optind = number(value);
1880 shellparam.optoff = -1;
1881}
1882#endif
1883
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884/*
1885 * Return of a legal variable name (a letter or underscore followed by zero or
1886 * more letters, underscores, and digits).
1887 */
1888static char *
1889endofname(const char *name)
1890{
1891 char *p;
1892
1893 p = (char *) name;
1894 if (!is_name(*p))
1895 return p;
1896 while (*++p) {
1897 if (!is_in_name(*p))
1898 break;
1899 }
1900 return p;
1901}
1902
1903/*
1904 * Compares two strings up to the first = or '\0'. The first
1905 * string must be terminated by '='; the second may be terminated by
1906 * either '=' or '\0'.
1907 */
1908static int
1909varcmp(const char *p, const char *q)
1910{
1911 int c, d;
1912
1913 while ((c = *p) == (d = *q)) {
1914 if (!c || c == '=')
1915 goto out;
1916 p++;
1917 q++;
1918 }
1919 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001920 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001922 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001923 out:
1924 return c - d;
1925}
1926
1927static int
1928varequal(const char *a, const char *b)
1929{
1930 return !varcmp(a, b);
1931}
1932
1933/*
1934 * Find the appropriate entry in the hash table from the name.
1935 */
1936static struct var **
1937hashvar(const char *p)
1938{
1939 unsigned hashval;
1940
1941 hashval = ((unsigned char) *p) << 4;
1942 while (*p && *p != '=')
1943 hashval += (unsigned char) *p++;
1944 return &vartab[hashval % VTABSIZE];
1945}
1946
1947static int
1948vpcmp(const void *a, const void *b)
1949{
1950 return varcmp(*(const char **)a, *(const char **)b);
1951}
1952
1953/*
1954 * This routine initializes the builtin variables.
1955 */
1956static void
1957initvar(void)
1958{
1959 struct var *vp;
1960 struct var *end;
1961 struct var **vpp;
1962
1963 /*
1964 * PS1 depends on uid
1965 */
1966#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1967 vps1.text = "PS1=\\w \\$ ";
1968#else
1969 if (!geteuid())
1970 vps1.text = "PS1=# ";
1971#endif
1972 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001973 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001974 do {
1975 vpp = hashvar(vp->text);
1976 vp->next = *vpp;
1977 *vpp = vp;
1978 } while (++vp < end);
1979}
1980
1981static struct var **
1982findvar(struct var **vpp, const char *name)
1983{
1984 for (; *vpp; vpp = &(*vpp)->next) {
1985 if (varequal((*vpp)->text, name)) {
1986 break;
1987 }
1988 }
1989 return vpp;
1990}
1991
1992/*
1993 * Find the value of a variable. Returns NULL if not set.
1994 */
1995static char *
1996lookupvar(const char *name)
1997{
1998 struct var *v;
1999
2000 v = *findvar(hashvar(name), name);
2001 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002002#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002003 /*
2004 * Dynamic variables are implemented roughly the same way they are
2005 * in bash. Namely, they're "special" so long as they aren't unset.
2006 * As soon as they're unset, they're no longer dynamic, and dynamic
2007 * lookup will no longer happen at that point. -- PFM.
2008 */
2009 if ((v->flags & VDYNAMIC))
2010 (*v->func)(NULL);
2011#endif
2012 if (!(v->flags & VUNSET))
2013 return strchrnul(v->text, '=') + 1;
2014 }
2015 return NULL;
2016}
2017
2018/*
2019 * Search the environment of a builtin command.
2020 */
2021static char *
2022bltinlookup(const char *name)
2023{
2024 struct strlist *sp;
2025
2026 for (sp = cmdenviron; sp; sp = sp->next) {
2027 if (varequal(sp->text, name))
2028 return strchrnul(sp->text, '=') + 1;
2029 }
2030 return lookupvar(name);
2031}
2032
2033/*
2034 * Same as setvar except that the variable and value are passed in
2035 * the first argument as name=value. Since the first argument will
2036 * be actually stored in the table, it should not be a string that
2037 * will go away.
2038 * Called with interrupts off.
2039 */
2040static void
2041setvareq(char *s, int flags)
2042{
2043 struct var *vp, **vpp;
2044
2045 vpp = hashvar(s);
2046 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2047 vp = *findvar(vpp, s);
2048 if (vp) {
2049 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2050 const char *n;
2051
2052 if (flags & VNOSAVE)
2053 free(s);
2054 n = vp->text;
2055 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2056 }
2057
2058 if (flags & VNOSET)
2059 return;
2060
2061 if (vp->func && (flags & VNOFUNC) == 0)
2062 (*vp->func)(strchrnul(s, '=') + 1);
2063
2064 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2065 free((char*)vp->text);
2066
2067 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2068 } else {
2069 if (flags & VNOSET)
2070 return;
2071 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002072 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002073 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002074 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002075 *vpp = vp;
2076 }
2077 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2078 s = ckstrdup(s);
2079 vp->text = s;
2080 vp->flags = flags;
2081}
2082
2083/*
2084 * Set the value of a variable. The flags argument is ored with the
2085 * flags of the variable. If val is NULL, the variable is unset.
2086 */
2087static void
2088setvar(const char *name, const char *val, int flags)
2089{
2090 char *p, *q;
2091 size_t namelen;
2092 char *nameeq;
2093 size_t vallen;
2094
2095 q = endofname(name);
2096 p = strchrnul(q, '=');
2097 namelen = p - name;
2098 if (!namelen || p != q)
2099 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2100 vallen = 0;
2101 if (val == NULL) {
2102 flags |= VUNSET;
2103 } else {
2104 vallen = strlen(val);
2105 }
2106 INT_OFF;
2107 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002108 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002109 if (val) {
2110 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002111 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112 }
2113 *p = '\0';
2114 setvareq(nameeq, flags | VNOSAVE);
2115 INT_ON;
2116}
2117
2118#if ENABLE_ASH_GETOPTS
2119/*
2120 * Safe version of setvar, returns 1 on success 0 on failure.
2121 */
2122static int
2123setvarsafe(const char *name, const char *val, int flags)
2124{
2125 int err;
2126 volatile int saveint;
2127 struct jmploc *volatile savehandler = exception_handler;
2128 struct jmploc jmploc;
2129
2130 SAVE_INT(saveint);
2131 if (setjmp(jmploc.loc))
2132 err = 1;
2133 else {
2134 exception_handler = &jmploc;
2135 setvar(name, val, flags);
2136 err = 0;
2137 }
2138 exception_handler = savehandler;
2139 RESTORE_INT(saveint);
2140 return err;
2141}
2142#endif
2143
2144/*
2145 * Unset the specified variable.
2146 */
2147static int
2148unsetvar(const char *s)
2149{
2150 struct var **vpp;
2151 struct var *vp;
2152 int retval;
2153
2154 vpp = findvar(hashvar(s), s);
2155 vp = *vpp;
2156 retval = 2;
2157 if (vp) {
2158 int flags = vp->flags;
2159
2160 retval = 1;
2161 if (flags & VREADONLY)
2162 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002163#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002164 vp->flags &= ~VDYNAMIC;
2165#endif
2166 if (flags & VUNSET)
2167 goto ok;
2168 if ((flags & VSTRFIXED) == 0) {
2169 INT_OFF;
2170 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2171 free((char*)vp->text);
2172 *vpp = vp->next;
2173 free(vp);
2174 INT_ON;
2175 } else {
2176 setvar(s, 0, 0);
2177 vp->flags &= ~VEXPORT;
2178 }
2179 ok:
2180 retval = 0;
2181 }
2182 out:
2183 return retval;
2184}
2185
2186/*
2187 * Process a linked list of variable assignments.
2188 */
2189static void
2190listsetvar(struct strlist *list_set_var, int flags)
2191{
2192 struct strlist *lp = list_set_var;
2193
2194 if (!lp)
2195 return;
2196 INT_OFF;
2197 do {
2198 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002199 lp = lp->next;
2200 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002201 INT_ON;
2202}
2203
2204/*
2205 * Generate a list of variables satisfying the given conditions.
2206 */
2207static char **
2208listvars(int on, int off, char ***end)
2209{
2210 struct var **vpp;
2211 struct var *vp;
2212 char **ep;
2213 int mask;
2214
2215 STARTSTACKSTR(ep);
2216 vpp = vartab;
2217 mask = on | off;
2218 do {
2219 for (vp = *vpp; vp; vp = vp->next) {
2220 if ((vp->flags & mask) == on) {
2221 if (ep == stackstrend())
2222 ep = growstackstr();
2223 *ep++ = (char *) vp->text;
2224 }
2225 }
2226 } while (++vpp < vartab + VTABSIZE);
2227 if (ep == stackstrend())
2228 ep = growstackstr();
2229 if (end)
2230 *end = ep;
2231 *ep++ = NULL;
2232 return grabstackstr(ep);
2233}
2234
2235
2236/* ============ Path search helper
2237 *
2238 * The variable path (passed by reference) should be set to the start
2239 * of the path before the first call; padvance will update
2240 * this value as it proceeds. Successive calls to padvance will return
2241 * the possible path expansions in sequence. If an option (indicated by
2242 * a percent sign) appears in the path entry then the global variable
2243 * pathopt will be set to point to it; otherwise pathopt will be set to
2244 * NULL.
2245 */
2246static const char *pathopt; /* set by padvance */
2247
2248static char *
2249padvance(const char **path, const char *name)
2250{
2251 const char *p;
2252 char *q;
2253 const char *start;
2254 size_t len;
2255
2256 if (*path == NULL)
2257 return NULL;
2258 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002259 for (p = start; *p && *p != ':' && *p != '%'; p++)
2260 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002261 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2262 while (stackblocksize() < len)
2263 growstackblock();
2264 q = stackblock();
2265 if (p != start) {
2266 memcpy(q, start, p - start);
2267 q += p - start;
2268 *q++ = '/';
2269 }
2270 strcpy(q, name);
2271 pathopt = NULL;
2272 if (*p == '%') {
2273 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002274 while (*p && *p != ':')
2275 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002276 }
2277 if (*p == ':')
2278 *path = p + 1;
2279 else
2280 *path = NULL;
2281 return stalloc(len);
2282}
2283
2284
2285/* ============ Prompt */
2286
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002287static smallint doprompt; /* if set, prompt the user */
2288static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002289
2290#if ENABLE_FEATURE_EDITING
2291static line_input_t *line_input_state;
2292static const char *cmdedit_prompt;
2293static void
2294putprompt(const char *s)
2295{
2296 if (ENABLE_ASH_EXPAND_PRMT) {
2297 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002298 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002299 return;
2300 }
2301 cmdedit_prompt = s;
2302}
2303#else
2304static void
2305putprompt(const char *s)
2306{
2307 out2str(s);
2308}
2309#endif
2310
2311#if ENABLE_ASH_EXPAND_PRMT
2312/* expandstr() needs parsing machinery, so it is far away ahead... */
2313static const char *expandstr(const char *ps);
2314#else
2315#define expandstr(s) s
2316#endif
2317
2318static void
2319setprompt(int whichprompt)
2320{
2321 const char *prompt;
2322#if ENABLE_ASH_EXPAND_PRMT
2323 struct stackmark smark;
2324#endif
2325
2326 needprompt = 0;
2327
2328 switch (whichprompt) {
2329 case 1:
2330 prompt = ps1val();
2331 break;
2332 case 2:
2333 prompt = ps2val();
2334 break;
2335 default: /* 0 */
2336 prompt = nullstr;
2337 }
2338#if ENABLE_ASH_EXPAND_PRMT
2339 setstackmark(&smark);
2340 stalloc(stackblocksize());
2341#endif
2342 putprompt(expandstr(prompt));
2343#if ENABLE_ASH_EXPAND_PRMT
2344 popstackmark(&smark);
2345#endif
2346}
2347
2348
2349/* ============ The cd and pwd commands */
2350
2351#define CD_PHYSICAL 1
2352#define CD_PRINT 2
2353
2354static int docd(const char *, int);
2355
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002356static int
2357cdopt(void)
2358{
2359 int flags = 0;
2360 int i, j;
2361
2362 j = 'L';
2363 while ((i = nextopt("LP"))) {
2364 if (i != j) {
2365 flags ^= CD_PHYSICAL;
2366 j = i;
2367 }
2368 }
2369
2370 return flags;
2371}
2372
2373/*
2374 * Update curdir (the name of the current directory) in response to a
2375 * cd command.
2376 */
2377static const char *
2378updatepwd(const char *dir)
2379{
2380 char *new;
2381 char *p;
2382 char *cdcomppath;
2383 const char *lim;
2384
2385 cdcomppath = ststrdup(dir);
2386 STARTSTACKSTR(new);
2387 if (*dir != '/') {
2388 if (curdir == nullstr)
2389 return 0;
2390 new = stack_putstr(curdir, new);
2391 }
2392 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002393 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002394 if (*dir != '/') {
2395 if (new[-1] != '/')
2396 USTPUTC('/', new);
2397 if (new > lim && *lim == '/')
2398 lim++;
2399 } else {
2400 USTPUTC('/', new);
2401 cdcomppath++;
2402 if (dir[1] == '/' && dir[2] != '/') {
2403 USTPUTC('/', new);
2404 cdcomppath++;
2405 lim++;
2406 }
2407 }
2408 p = strtok(cdcomppath, "/");
2409 while (p) {
2410 switch (*p) {
2411 case '.':
2412 if (p[1] == '.' && p[2] == '\0') {
2413 while (new > lim) {
2414 STUNPUTC(new);
2415 if (new[-1] == '/')
2416 break;
2417 }
2418 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002419 }
2420 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002421 break;
2422 /* fall through */
2423 default:
2424 new = stack_putstr(p, new);
2425 USTPUTC('/', new);
2426 }
2427 p = strtok(0, "/");
2428 }
2429 if (new > lim)
2430 STUNPUTC(new);
2431 *new = 0;
2432 return stackblock();
2433}
2434
2435/*
2436 * Find out what the current directory is. If we already know the current
2437 * directory, this routine returns immediately.
2438 */
2439static char *
2440getpwd(void)
2441{
Denis Vlasenko01631112007-12-16 17:20:38 +00002442 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002443 return dir ? dir : nullstr;
2444}
2445
2446static void
2447setpwd(const char *val, int setold)
2448{
2449 char *oldcur, *dir;
2450
2451 oldcur = dir = curdir;
2452
2453 if (setold) {
2454 setvar("OLDPWD", oldcur, VEXPORT);
2455 }
2456 INT_OFF;
2457 if (physdir != nullstr) {
2458 if (physdir != oldcur)
2459 free(physdir);
2460 physdir = nullstr;
2461 }
2462 if (oldcur == val || !val) {
2463 char *s = getpwd();
2464 physdir = s;
2465 if (!val)
2466 dir = s;
2467 } else
2468 dir = ckstrdup(val);
2469 if (oldcur != dir && oldcur != nullstr) {
2470 free(oldcur);
2471 }
2472 curdir = dir;
2473 INT_ON;
2474 setvar("PWD", dir, VEXPORT);
2475}
2476
2477static void hashcd(void);
2478
2479/*
2480 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2481 * know that the current directory has changed.
2482 */
2483static int
2484docd(const char *dest, int flags)
2485{
2486 const char *dir = 0;
2487 int err;
2488
2489 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2490
2491 INT_OFF;
2492 if (!(flags & CD_PHYSICAL)) {
2493 dir = updatepwd(dest);
2494 if (dir)
2495 dest = dir;
2496 }
2497 err = chdir(dest);
2498 if (err)
2499 goto out;
2500 setpwd(dir, 1);
2501 hashcd();
2502 out:
2503 INT_ON;
2504 return err;
2505}
2506
2507static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002508cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002509{
2510 const char *dest;
2511 const char *path;
2512 const char *p;
2513 char c;
2514 struct stat statb;
2515 int flags;
2516
2517 flags = cdopt();
2518 dest = *argptr;
2519 if (!dest)
2520 dest = bltinlookup(homestr);
2521 else if (LONE_DASH(dest)) {
2522 dest = bltinlookup("OLDPWD");
2523 flags |= CD_PRINT;
2524 }
2525 if (!dest)
2526 dest = nullstr;
2527 if (*dest == '/')
2528 goto step7;
2529 if (*dest == '.') {
2530 c = dest[1];
2531 dotdot:
2532 switch (c) {
2533 case '\0':
2534 case '/':
2535 goto step6;
2536 case '.':
2537 c = dest[2];
2538 if (c != '.')
2539 goto dotdot;
2540 }
2541 }
2542 if (!*dest)
2543 dest = ".";
2544 path = bltinlookup("CDPATH");
2545 if (!path) {
2546 step6:
2547 step7:
2548 p = dest;
2549 goto docd;
2550 }
2551 do {
2552 c = *path;
2553 p = padvance(&path, dest);
2554 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2555 if (c && c != ':')
2556 flags |= CD_PRINT;
2557 docd:
2558 if (!docd(p, flags))
2559 goto out;
2560 break;
2561 }
2562 } while (path);
2563 ash_msg_and_raise_error("can't cd to %s", dest);
2564 /* NOTREACHED */
2565 out:
2566 if (flags & CD_PRINT)
2567 out1fmt(snlfmt, curdir);
2568 return 0;
2569}
2570
2571static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002572pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002573{
2574 int flags;
2575 const char *dir = curdir;
2576
2577 flags = cdopt();
2578 if (flags) {
2579 if (physdir == nullstr)
2580 setpwd(dir, 0);
2581 dir = physdir;
2582 }
2583 out1fmt(snlfmt, dir);
2584 return 0;
2585}
2586
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002587
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002588/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002589
Denis Vlasenko834dee72008-10-07 09:18:30 +00002590
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002591#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002592/* buffer for top level input file */
2593#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002594
Eric Andersenc470f442003-07-28 09:56:35 +00002595/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002596#define CWORD 0 /* character is nothing special */
2597#define CNL 1 /* newline character */
2598#define CBACK 2 /* a backslash character */
2599#define CSQUOTE 3 /* single quote */
2600#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002601#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002602#define CBQUOTE 6 /* backwards single quote */
2603#define CVAR 7 /* a dollar sign */
2604#define CENDVAR 8 /* a '}' character */
2605#define CLP 9 /* a left paren in arithmetic */
2606#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002607#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002608#define CCTL 12 /* like CWORD, except it must be escaped */
2609#define CSPCL 13 /* these terminate a word */
2610#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002611
Denis Vlasenko131ae172007-02-18 13:00:19 +00002612#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002613#define SYNBASE 130
2614#define PEOF -130
2615#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002616#define PEOA_OR_PEOF PEOA
2617#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002618#define SYNBASE 129
2619#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002620#define PEOA_OR_PEOF PEOF
2621#endif
2622
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002623/* number syntax index */
2624#define BASESYNTAX 0 /* not in quotes */
2625#define DQSYNTAX 1 /* in double quotes */
2626#define SQSYNTAX 2 /* in single quotes */
2627#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002628#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002629
Denis Vlasenko131ae172007-02-18 13:00:19 +00002630#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002631#define USE_SIT_FUNCTION
2632#endif
2633
Denis Vlasenko131ae172007-02-18 13:00:19 +00002634#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002635static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002636#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002637 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002638#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002639 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2640 { CNL, CNL, CNL, CNL }, /* 2, \n */
2641 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2642 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2643 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2644 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2645 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2646 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2647 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2648 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2649 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002650#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002651 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2652 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2653 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002654#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002655};
Eric Andersenc470f442003-07-28 09:56:35 +00002656#else
2657static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002658#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002659 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002660#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002661 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2662 { CNL, CNL, CNL }, /* 2, \n */
2663 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2664 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2665 { CVAR, CVAR, CWORD }, /* 5, $ */
2666 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2667 { CSPCL, CWORD, CWORD }, /* 7, ( */
2668 { CSPCL, CWORD, CWORD }, /* 8, ) */
2669 { CBACK, CBACK, CCTL }, /* 9, \ */
2670 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2671 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002672#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002673 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2674 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2675 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002676#endif
2677};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002678#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002679
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002680#ifdef USE_SIT_FUNCTION
2681
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002682static int
2683SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002684{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002685 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002686#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002687 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002688 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2689 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2690 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2691 11, 3 /* "}~" */
2692 };
2693#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002694 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002695 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2696 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2697 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2698 10, 2 /* "}~" */
2699 };
2700#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002701 const char *s;
2702 int indx;
2703
Eric Andersenc470f442003-07-28 09:56:35 +00002704 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002705 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002706#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002707 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002708 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002709 else
2710#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002711
2712 if ((unsigned char)c >= (unsigned char)(CTLESC)
2713 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2714 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002715 return CCTL;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002716 }
Denis Vlasenko68819d12008-12-15 11:26:36 +00002717 s = strchrnul(spec_symbls, c);
2718 if (*s == '\0')
2719 return CWORD;
2720 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002721 return S_I_T[indx][syntax];
2722}
2723
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002724#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002725
Denis Vlasenko131ae172007-02-18 13:00:19 +00002726#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002727#define CSPCL_CIGN_CIGN_CIGN 0
2728#define CSPCL_CWORD_CWORD_CWORD 1
2729#define CNL_CNL_CNL_CNL 2
2730#define CWORD_CCTL_CCTL_CWORD 3
2731#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2732#define CVAR_CVAR_CWORD_CVAR 5
2733#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2734#define CSPCL_CWORD_CWORD_CLP 7
2735#define CSPCL_CWORD_CWORD_CRP 8
2736#define CBACK_CBACK_CCTL_CBACK 9
2737#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2738#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2739#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2740#define CWORD_CWORD_CWORD_CWORD 13
2741#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002742#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002743#define CSPCL_CWORD_CWORD_CWORD 0
2744#define CNL_CNL_CNL_CNL 1
2745#define CWORD_CCTL_CCTL_CWORD 2
2746#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2747#define CVAR_CVAR_CWORD_CVAR 4
2748#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2749#define CSPCL_CWORD_CWORD_CLP 6
2750#define CSPCL_CWORD_CWORD_CRP 7
2751#define CBACK_CBACK_CCTL_CBACK 8
2752#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2753#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2754#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2755#define CWORD_CWORD_CWORD_CWORD 12
2756#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002757#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002758
2759static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002760 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002761 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002762#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002763 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2764#endif
2765 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2766 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2767 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2768 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2769 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2770 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2771 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2772 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2773 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002774 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2775 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2776 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2777 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2778 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2779 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2780 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2903 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2904 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2909 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2910 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2913 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2914 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2915 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2916 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2917 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2918 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2919 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2920 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2921 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2922 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2923 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2924 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2925 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2926 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002927 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002928 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2930 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002932 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002933 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2934 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2935 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2936 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2938 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2939 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2940 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2941 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2952 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2953 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2954 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2955 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2956 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2957 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2985 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2986 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2987 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2990 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2991 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2992 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2993 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2996 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3005 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3006 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3007 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3008 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3009 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3010 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3011 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3012 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3013 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3014 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3015 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3016 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3017 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3018 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3019 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3020 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003021};
3022
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003023#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3024
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003025#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003026
Eric Andersen2870d962001-07-02 17:27:21 +00003027
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003028/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003029
Denis Vlasenko131ae172007-02-18 13:00:19 +00003030#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003031
3032#define ALIASINUSE 1
3033#define ALIASDEAD 2
3034
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003035struct alias {
3036 struct alias *next;
3037 char *name;
3038 char *val;
3039 int flag;
3040};
3041
Denis Vlasenko01631112007-12-16 17:20:38 +00003042
3043static struct alias **atab; // [ATABSIZE];
3044#define INIT_G_alias() do { \
3045 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3046} while (0)
3047
Eric Andersen2870d962001-07-02 17:27:21 +00003048
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003049static struct alias **
3050__lookupalias(const char *name) {
3051 unsigned int hashval;
3052 struct alias **app;
3053 const char *p;
3054 unsigned int ch;
3055
3056 p = name;
3057
3058 ch = (unsigned char)*p;
3059 hashval = ch << 4;
3060 while (ch) {
3061 hashval += ch;
3062 ch = (unsigned char)*++p;
3063 }
3064 app = &atab[hashval % ATABSIZE];
3065
3066 for (; *app; app = &(*app)->next) {
3067 if (strcmp(name, (*app)->name) == 0) {
3068 break;
3069 }
3070 }
3071
3072 return app;
3073}
3074
3075static struct alias *
3076lookupalias(const char *name, int check)
3077{
3078 struct alias *ap = *__lookupalias(name);
3079
3080 if (check && ap && (ap->flag & ALIASINUSE))
3081 return NULL;
3082 return ap;
3083}
3084
3085static struct alias *
3086freealias(struct alias *ap)
3087{
3088 struct alias *next;
3089
3090 if (ap->flag & ALIASINUSE) {
3091 ap->flag |= ALIASDEAD;
3092 return ap;
3093 }
3094
3095 next = ap->next;
3096 free(ap->name);
3097 free(ap->val);
3098 free(ap);
3099 return next;
3100}
Eric Andersencb57d552001-06-28 07:25:16 +00003101
Eric Andersenc470f442003-07-28 09:56:35 +00003102static void
3103setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003104{
3105 struct alias *ap, **app;
3106
3107 app = __lookupalias(name);
3108 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003109 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003110 if (ap) {
3111 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003112 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003113 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003114 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003115 ap->flag &= ~ALIASDEAD;
3116 } else {
3117 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003118 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003119 ap->name = ckstrdup(name);
3120 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003121 /*ap->flag = 0; - ckzalloc did it */
3122 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003123 *app = ap;
3124 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003125 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003126}
3127
Eric Andersenc470f442003-07-28 09:56:35 +00003128static int
3129unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003130{
Eric Andersencb57d552001-06-28 07:25:16 +00003131 struct alias **app;
3132
3133 app = __lookupalias(name);
3134
3135 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003136 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003137 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003138 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003139 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003140 }
3141
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003142 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003143}
3144
Eric Andersenc470f442003-07-28 09:56:35 +00003145static void
3146rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003147{
Eric Andersencb57d552001-06-28 07:25:16 +00003148 struct alias *ap, **app;
3149 int i;
3150
Denis Vlasenkob012b102007-02-19 22:43:01 +00003151 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003152 for (i = 0; i < ATABSIZE; i++) {
3153 app = &atab[i];
3154 for (ap = *app; ap; ap = *app) {
3155 *app = freealias(*app);
3156 if (ap == *app) {
3157 app = &ap->next;
3158 }
3159 }
3160 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003161 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003162}
3163
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003164static void
3165printalias(const struct alias *ap)
3166{
3167 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3168}
3169
Eric Andersencb57d552001-06-28 07:25:16 +00003170/*
3171 * TODO - sort output
3172 */
Eric Andersenc470f442003-07-28 09:56:35 +00003173static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003174aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003175{
3176 char *n, *v;
3177 int ret = 0;
3178 struct alias *ap;
3179
Denis Vlasenko68404f12008-03-17 09:00:54 +00003180 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003181 int i;
3182
Denis Vlasenko68404f12008-03-17 09:00:54 +00003183 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003184 for (ap = atab[i]; ap; ap = ap->next) {
3185 printalias(ap);
3186 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003187 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003188 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003189 }
3190 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003191 v = strchr(n+1, '=');
3192 if (v == NULL) { /* n+1: funny ksh stuff */
3193 ap = *__lookupalias(n);
3194 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003195 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003196 ret = 1;
3197 } else
3198 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003199 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003200 *v++ = '\0';
3201 setalias(n, v);
3202 }
3203 }
3204
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003205 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003206}
3207
Eric Andersenc470f442003-07-28 09:56:35 +00003208static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003209unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003210{
3211 int i;
3212
3213 while ((i = nextopt("a")) != '\0') {
3214 if (i == 'a') {
3215 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003216 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003217 }
3218 }
3219 for (i = 0; *argptr; argptr++) {
3220 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003221 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003222 i = 1;
3223 }
3224 }
3225
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003226 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003227}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003228
Denis Vlasenko131ae172007-02-18 13:00:19 +00003229#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003230
Eric Andersenc470f442003-07-28 09:56:35 +00003231
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003232/* ============ jobs.c */
3233
3234/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3235#define FORK_FG 0
3236#define FORK_BG 1
3237#define FORK_NOJOB 2
3238
3239/* mode flags for showjob(s) */
3240#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3241#define SHOW_PID 0x04 /* include process pid */
3242#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3243
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003244/*
3245 * A job structure contains information about a job. A job is either a
3246 * single process or a set of processes contained in a pipeline. In the
3247 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3248 * array of pids.
3249 */
3250
3251struct procstat {
3252 pid_t pid; /* process id */
3253 int status; /* last process status from wait() */
3254 char *cmd; /* text of command being run */
3255};
3256
3257struct job {
3258 struct procstat ps0; /* status of process */
3259 struct procstat *ps; /* status or processes when more than one */
3260#if JOBS
3261 int stopstatus; /* status of a stopped job */
3262#endif
3263 uint32_t
3264 nprocs: 16, /* number of processes */
3265 state: 8,
3266#define JOBRUNNING 0 /* at least one proc running */
3267#define JOBSTOPPED 1 /* all procs are stopped */
3268#define JOBDONE 2 /* all procs are completed */
3269#if JOBS
3270 sigint: 1, /* job was killed by SIGINT */
3271 jobctl: 1, /* job running under job control */
3272#endif
3273 waited: 1, /* true if this entry has been waited for */
3274 used: 1, /* true if this entry is in used */
3275 changed: 1; /* true if status has changed */
3276 struct job *prev_job; /* previous job */
3277};
3278
Denis Vlasenko68404f12008-03-17 09:00:54 +00003279static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003280#if !JOBS
3281#define forkshell(job, node, mode) forkshell(job, mode)
3282#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003283static int forkshell(struct job *, union node *, int);
3284static int waitforjob(struct job *);
3285
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003286#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003287enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003288#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003289#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003290static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003291static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003292#endif
3293
3294/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003295 * Ignore a signal.
3296 */
3297static void
3298ignoresig(int signo)
3299{
3300 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3301 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3302 /* No, need to do it */
3303 signal(signo, SIG_IGN);
3304 }
3305 sigmode[signo - 1] = S_HARD_IGN;
3306}
3307
3308/*
3309 * Signal handler. Only one usage site - in setsignal()
3310 */
3311static void
3312onsig(int signo)
3313{
3314 gotsig[signo - 1] = 1;
3315
3316 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
3317 if (!suppressint) {
3318 pendingsig = 0;
3319 raise_interrupt(); /* does not return */
3320 }
3321 intpending = 1;
3322 } else {
3323 pendingsig = signo;
3324 }
3325}
3326
3327/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003328 * Set the signal handler for the specified signal. The routine figures
3329 * out what it should be set to.
3330 */
3331static void
3332setsignal(int signo)
3333{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003334 char *t;
3335 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003336 struct sigaction act;
3337
3338 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003339 new_act = S_DFL;
3340 if (t != NULL) { /* trap for this sig is set */
3341 new_act = S_CATCH;
3342 if (t[0] == '\0') /* trap is "": ignore this sig */
3343 new_act = S_IGN;
3344 }
3345
3346 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003347 switch (signo) {
3348 case SIGINT:
3349 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003350 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003351 break;
3352 case SIGQUIT:
3353#if DEBUG
3354 if (debug)
3355 break;
3356#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003357 /* man bash:
3358 * "In all cases, bash ignores SIGQUIT. Non-builtin
3359 * commands run by bash have signal handlers
3360 * set to the values inherited by the shell
3361 * from its parent". */
3362 new_act = S_IGN;
3363 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364 case SIGTERM:
3365 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003366 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003367 break;
3368#if JOBS
3369 case SIGTSTP:
3370 case SIGTTOU:
3371 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003372 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373 break;
3374#endif
3375 }
3376 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003377//TODO: if !rootshell, we reset SIGQUIT to DFL,
3378//whereas we have to restore it to what shell got on entry
3379//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380
3381 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003382 cur_act = *t;
3383 if (cur_act == 0) {
3384 /* current setting is not yet known */
3385 if (sigaction(signo, NULL, &act)) {
3386 /* pretend it worked; maybe we should give a warning,
3387 * but other shells don't. We don't alter sigmode,
3388 * so we retry every time.
3389 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003390 return;
3391 }
3392 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003393 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003394 if (mflag
3395 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3396 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003397 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003398 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003399 }
3400 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003401 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003402 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003403
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003404 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003405 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003406 case S_CATCH:
3407 act.sa_handler = onsig;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003408 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3409 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003410 break;
3411 case S_IGN:
3412 act.sa_handler = SIG_IGN;
3413 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003414 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003415 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003416
3417 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003418}
3419
3420/* mode flags for set_curjob */
3421#define CUR_DELETE 2
3422#define CUR_RUNNING 1
3423#define CUR_STOPPED 0
3424
3425/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003426#define DOWAIT_NONBLOCK WNOHANG
3427#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003428
3429#if JOBS
3430/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003431static int initialpgrp; //references:2
3432static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003433#endif
3434/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003435static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003436/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003437static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003438/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003439static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003441static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003442
3443static void
3444set_curjob(struct job *jp, unsigned mode)
3445{
3446 struct job *jp1;
3447 struct job **jpp, **curp;
3448
3449 /* first remove from list */
3450 jpp = curp = &curjob;
3451 do {
3452 jp1 = *jpp;
3453 if (jp1 == jp)
3454 break;
3455 jpp = &jp1->prev_job;
3456 } while (1);
3457 *jpp = jp1->prev_job;
3458
3459 /* Then re-insert in correct position */
3460 jpp = curp;
3461 switch (mode) {
3462 default:
3463#if DEBUG
3464 abort();
3465#endif
3466 case CUR_DELETE:
3467 /* job being deleted */
3468 break;
3469 case CUR_RUNNING:
3470 /* newly created job or backgrounded job,
3471 put after all stopped jobs. */
3472 do {
3473 jp1 = *jpp;
3474#if JOBS
3475 if (!jp1 || jp1->state != JOBSTOPPED)
3476#endif
3477 break;
3478 jpp = &jp1->prev_job;
3479 } while (1);
3480 /* FALLTHROUGH */
3481#if JOBS
3482 case CUR_STOPPED:
3483#endif
3484 /* newly stopped job - becomes curjob */
3485 jp->prev_job = *jpp;
3486 *jpp = jp;
3487 break;
3488 }
3489}
3490
3491#if JOBS || DEBUG
3492static int
3493jobno(const struct job *jp)
3494{
3495 return jp - jobtab + 1;
3496}
3497#endif
3498
3499/*
3500 * Convert a job name to a job structure.
3501 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003502#if !JOBS
3503#define getjob(name, getctl) getjob(name)
3504#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003505static struct job *
3506getjob(const char *name, int getctl)
3507{
3508 struct job *jp;
3509 struct job *found;
3510 const char *err_msg = "No such job: %s";
3511 unsigned num;
3512 int c;
3513 const char *p;
3514 char *(*match)(const char *, const char *);
3515
3516 jp = curjob;
3517 p = name;
3518 if (!p)
3519 goto currentjob;
3520
3521 if (*p != '%')
3522 goto err;
3523
3524 c = *++p;
3525 if (!c)
3526 goto currentjob;
3527
3528 if (!p[1]) {
3529 if (c == '+' || c == '%') {
3530 currentjob:
3531 err_msg = "No current job";
3532 goto check;
3533 }
3534 if (c == '-') {
3535 if (jp)
3536 jp = jp->prev_job;
3537 err_msg = "No previous job";
3538 check:
3539 if (!jp)
3540 goto err;
3541 goto gotit;
3542 }
3543 }
3544
3545 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003546// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003547 num = atoi(p);
3548 if (num < njobs) {
3549 jp = jobtab + num - 1;
3550 if (jp->used)
3551 goto gotit;
3552 goto err;
3553 }
3554 }
3555
3556 match = prefix;
3557 if (*p == '?') {
3558 match = strstr;
3559 p++;
3560 }
3561
3562 found = 0;
3563 while (1) {
3564 if (!jp)
3565 goto err;
3566 if (match(jp->ps[0].cmd, p)) {
3567 if (found)
3568 goto err;
3569 found = jp;
3570 err_msg = "%s: ambiguous";
3571 }
3572 jp = jp->prev_job;
3573 }
3574
3575 gotit:
3576#if JOBS
3577 err_msg = "job %s not created under job control";
3578 if (getctl && jp->jobctl == 0)
3579 goto err;
3580#endif
3581 return jp;
3582 err:
3583 ash_msg_and_raise_error(err_msg, name);
3584}
3585
3586/*
3587 * Mark a job structure as unused.
3588 */
3589static void
3590freejob(struct job *jp)
3591{
3592 struct procstat *ps;
3593 int i;
3594
3595 INT_OFF;
3596 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3597 if (ps->cmd != nullstr)
3598 free(ps->cmd);
3599 }
3600 if (jp->ps != &jp->ps0)
3601 free(jp->ps);
3602 jp->used = 0;
3603 set_curjob(jp, CUR_DELETE);
3604 INT_ON;
3605}
3606
3607#if JOBS
3608static void
3609xtcsetpgrp(int fd, pid_t pgrp)
3610{
3611 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003612 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003613}
3614
3615/*
3616 * Turn job control on and off.
3617 *
3618 * Note: This code assumes that the third arg to ioctl is a character
3619 * pointer, which is true on Berkeley systems but not System V. Since
3620 * System V doesn't have job control yet, this isn't a problem now.
3621 *
3622 * Called with interrupts off.
3623 */
3624static void
3625setjobctl(int on)
3626{
3627 int fd;
3628 int pgrp;
3629
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003630 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003631 return;
3632 if (on) {
3633 int ofd;
3634 ofd = fd = open(_PATH_TTY, O_RDWR);
3635 if (fd < 0) {
3636 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3637 * That sometimes helps to acquire controlling tty.
3638 * Obviously, a workaround for bugs when someone
3639 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003640 fd = 2;
3641 while (!isatty(fd))
3642 if (--fd < 0)
3643 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003644 }
3645 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003646 if (ofd >= 0)
3647 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003648 if (fd < 0)
3649 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003650 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003651 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003652 do { /* while we are in the background */
3653 pgrp = tcgetpgrp(fd);
3654 if (pgrp < 0) {
3655 out:
3656 ash_msg("can't access tty; job control turned off");
3657 mflag = on = 0;
3658 goto close;
3659 }
3660 if (pgrp == getpgrp())
3661 break;
3662 killpg(0, SIGTTIN);
3663 } while (1);
3664 initialpgrp = pgrp;
3665
3666 setsignal(SIGTSTP);
3667 setsignal(SIGTTOU);
3668 setsignal(SIGTTIN);
3669 pgrp = rootpid;
3670 setpgid(0, pgrp);
3671 xtcsetpgrp(fd, pgrp);
3672 } else {
3673 /* turning job control off */
3674 fd = ttyfd;
3675 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003676 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003677 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003678 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003679 setpgid(0, pgrp);
3680 setsignal(SIGTSTP);
3681 setsignal(SIGTTOU);
3682 setsignal(SIGTTIN);
3683 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003684 if (fd >= 0)
3685 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686 fd = -1;
3687 }
3688 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003689 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003690}
3691
3692static int
3693killcmd(int argc, char **argv)
3694{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003695 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003696 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003697 do {
3698 if (argv[i][0] == '%') {
3699 struct job *jp = getjob(argv[i], 0);
3700 unsigned pid = jp->ps[0].pid;
3701 /* Enough space for ' -NNN<nul>' */
3702 argv[i] = alloca(sizeof(int)*3 + 3);
3703 /* kill_main has matching code to expect
3704 * leading space. Needed to not confuse
3705 * negative pids with "kill -SIGNAL_NO" syntax */
3706 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003707 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003708 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003709 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003710 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003711}
3712
3713static void
3714showpipe(struct job *jp, FILE *out)
3715{
3716 struct procstat *sp;
3717 struct procstat *spend;
3718
3719 spend = jp->ps + jp->nprocs;
3720 for (sp = jp->ps + 1; sp < spend; sp++)
3721 fprintf(out, " | %s", sp->cmd);
3722 outcslow('\n', out);
3723 flush_stdout_stderr();
3724}
3725
3726
3727static int
3728restartjob(struct job *jp, int mode)
3729{
3730 struct procstat *ps;
3731 int i;
3732 int status;
3733 pid_t pgid;
3734
3735 INT_OFF;
3736 if (jp->state == JOBDONE)
3737 goto out;
3738 jp->state = JOBRUNNING;
3739 pgid = jp->ps->pid;
3740 if (mode == FORK_FG)
3741 xtcsetpgrp(ttyfd, pgid);
3742 killpg(pgid, SIGCONT);
3743 ps = jp->ps;
3744 i = jp->nprocs;
3745 do {
3746 if (WIFSTOPPED(ps->status)) {
3747 ps->status = -1;
3748 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003749 ps++;
3750 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003751 out:
3752 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3753 INT_ON;
3754 return status;
3755}
3756
3757static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003758fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759{
3760 struct job *jp;
3761 FILE *out;
3762 int mode;
3763 int retval;
3764
3765 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3766 nextopt(nullstr);
3767 argv = argptr;
3768 out = stdout;
3769 do {
3770 jp = getjob(*argv, 1);
3771 if (mode == FORK_BG) {
3772 set_curjob(jp, CUR_RUNNING);
3773 fprintf(out, "[%d] ", jobno(jp));
3774 }
3775 outstr(jp->ps->cmd, out);
3776 showpipe(jp, out);
3777 retval = restartjob(jp, mode);
3778 } while (*argv && *++argv);
3779 return retval;
3780}
3781#endif
3782
3783static int
3784sprint_status(char *s, int status, int sigonly)
3785{
3786 int col;
3787 int st;
3788
3789 col = 0;
3790 if (!WIFEXITED(status)) {
3791#if JOBS
3792 if (WIFSTOPPED(status))
3793 st = WSTOPSIG(status);
3794 else
3795#endif
3796 st = WTERMSIG(status);
3797 if (sigonly) {
3798 if (st == SIGINT || st == SIGPIPE)
3799 goto out;
3800#if JOBS
3801 if (WIFSTOPPED(status))
3802 goto out;
3803#endif
3804 }
3805 st &= 0x7f;
3806 col = fmtstr(s, 32, strsignal(st));
3807 if (WCOREDUMP(status)) {
3808 col += fmtstr(s + col, 16, " (core dumped)");
3809 }
3810 } else if (!sigonly) {
3811 st = WEXITSTATUS(status);
3812 if (st)
3813 col = fmtstr(s, 16, "Done(%d)", st);
3814 else
3815 col = fmtstr(s, 16, "Done");
3816 }
3817 out:
3818 return col;
3819}
3820
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003822dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003823{
3824 int pid;
3825 int status;
3826 struct job *jp;
3827 struct job *thisjob;
3828 int state;
3829
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003830 TRACE(("dowait(0x%x) called\n", wait_flags));
3831
3832 /* Do a wait system call. If job control is compiled in, we accept
3833 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3834 * NB: _not_ safe_waitpid, we need to detect EINTR */
3835 pid = waitpid(-1, &status,
3836 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3837 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003838 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003840
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003841 INT_OFF;
3842 thisjob = NULL;
3843 for (jp = curjob; jp; jp = jp->prev_job) {
3844 struct procstat *sp;
3845 struct procstat *spend;
3846 if (jp->state == JOBDONE)
3847 continue;
3848 state = JOBDONE;
3849 spend = jp->ps + jp->nprocs;
3850 sp = jp->ps;
3851 do {
3852 if (sp->pid == pid) {
3853 TRACE(("Job %d: changing status of proc %d "
3854 "from 0x%x to 0x%x\n",
3855 jobno(jp), pid, sp->status, status));
3856 sp->status = status;
3857 thisjob = jp;
3858 }
3859 if (sp->status == -1)
3860 state = JOBRUNNING;
3861#if JOBS
3862 if (state == JOBRUNNING)
3863 continue;
3864 if (WIFSTOPPED(sp->status)) {
3865 jp->stopstatus = sp->status;
3866 state = JOBSTOPPED;
3867 }
3868#endif
3869 } while (++sp < spend);
3870 if (thisjob)
3871 goto gotjob;
3872 }
3873#if JOBS
3874 if (!WIFSTOPPED(status))
3875#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876 jobless--;
3877 goto out;
3878
3879 gotjob:
3880 if (state != JOBRUNNING) {
3881 thisjob->changed = 1;
3882
3883 if (thisjob->state != state) {
3884 TRACE(("Job %d: changing state from %d to %d\n",
3885 jobno(thisjob), thisjob->state, state));
3886 thisjob->state = state;
3887#if JOBS
3888 if (state == JOBSTOPPED) {
3889 set_curjob(thisjob, CUR_STOPPED);
3890 }
3891#endif
3892 }
3893 }
3894
3895 out:
3896 INT_ON;
3897
3898 if (thisjob && thisjob == job) {
3899 char s[48 + 1];
3900 int len;
3901
3902 len = sprint_status(s, status, 1);
3903 if (len) {
3904 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003905 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003906 out2str(s);
3907 }
3908 }
3909 return pid;
3910}
3911
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003912static int
3913blocking_wait_with_raise_on_sig(struct job *job)
3914{
3915 pid_t pid = dowait(DOWAIT_BLOCK, job);
3916 if (pid <= 0 && pendingsig)
3917 raise_exception(EXSIG);
3918 return pid;
3919}
3920
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003921#if JOBS
3922static void
3923showjob(FILE *out, struct job *jp, int mode)
3924{
3925 struct procstat *ps;
3926 struct procstat *psend;
3927 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003928 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003929 char s[80];
3930
3931 ps = jp->ps;
3932
3933 if (mode & SHOW_PGID) {
3934 /* just output process (group) id of pipeline */
3935 fprintf(out, "%d\n", ps->pid);
3936 return;
3937 }
3938
3939 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003940 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003941
3942 if (jp == curjob)
3943 s[col - 2] = '+';
3944 else if (curjob && jp == curjob->prev_job)
3945 s[col - 2] = '-';
3946
3947 if (mode & SHOW_PID)
3948 col += fmtstr(s + col, 16, "%d ", ps->pid);
3949
3950 psend = ps + jp->nprocs;
3951
3952 if (jp->state == JOBRUNNING) {
3953 strcpy(s + col, "Running");
3954 col += sizeof("Running") - 1;
3955 } else {
3956 int status = psend[-1].status;
3957 if (jp->state == JOBSTOPPED)
3958 status = jp->stopstatus;
3959 col += sprint_status(s + col, status, 0);
3960 }
3961
3962 goto start;
3963
3964 do {
3965 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003966 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003967 start:
3968 fprintf(out, "%s%*c%s",
3969 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3970 );
3971 if (!(mode & SHOW_PID)) {
3972 showpipe(jp, out);
3973 break;
3974 }
3975 if (++ps == psend) {
3976 outcslow('\n', out);
3977 break;
3978 }
3979 } while (1);
3980
3981 jp->changed = 0;
3982
3983 if (jp->state == JOBDONE) {
3984 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3985 freejob(jp);
3986 }
3987}
3988
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003989/*
3990 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3991 * statuses have changed since the last call to showjobs.
3992 */
3993static void
3994showjobs(FILE *out, int mode)
3995{
3996 struct job *jp;
3997
3998 TRACE(("showjobs(%x) called\n", mode));
3999
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004000 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004001 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002 continue;
4003
4004 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004005 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004006 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004007 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004008 }
4009}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004010
4011static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004012jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004013{
4014 int mode, m;
4015
4016 mode = 0;
4017 while ((m = nextopt("lp"))) {
4018 if (m == 'l')
4019 mode = SHOW_PID;
4020 else
4021 mode = SHOW_PGID;
4022 }
4023
4024 argv = argptr;
4025 if (*argv) {
4026 do
4027 showjob(stdout, getjob(*argv,0), mode);
4028 while (*++argv);
4029 } else
4030 showjobs(stdout, mode);
4031
4032 return 0;
4033}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034#endif /* JOBS */
4035
4036static int
4037getstatus(struct job *job)
4038{
4039 int status;
4040 int retval;
4041
4042 status = job->ps[job->nprocs - 1].status;
4043 retval = WEXITSTATUS(status);
4044 if (!WIFEXITED(status)) {
4045#if JOBS
4046 retval = WSTOPSIG(status);
4047 if (!WIFSTOPPED(status))
4048#endif
4049 {
4050 /* XXX: limits number of signals */
4051 retval = WTERMSIG(status);
4052#if JOBS
4053 if (retval == SIGINT)
4054 job->sigint = 1;
4055#endif
4056 }
4057 retval += 128;
4058 }
4059 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4060 jobno(job), job->nprocs, status, retval));
4061 return retval;
4062}
4063
4064static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004065waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066{
4067 struct job *job;
4068 int retval;
4069 struct job *jp;
4070
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004071// exsig++;
4072// xbarrier();
4073 if (pendingsig)
4074 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075
4076 nextopt(nullstr);
4077 retval = 0;
4078
4079 argv = argptr;
4080 if (!*argv) {
4081 /* wait for all jobs */
4082 for (;;) {
4083 jp = curjob;
4084 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004085 if (!jp) /* no running procs */
4086 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087 if (jp->state == JOBRUNNING)
4088 break;
4089 jp->waited = 1;
4090 jp = jp->prev_job;
4091 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004092 /* man bash:
4093 * "When bash is waiting for an asynchronous command via
4094 * the wait builtin, the reception of a signal for which a trap
4095 * has been set will cause the wait builtin to return immediately
4096 * with an exit status greater than 128, immediately after which
4097 * the trap is executed."
4098 * Do we do it that way? */
4099 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004100 }
4101 }
4102
4103 retval = 127;
4104 do {
4105 if (**argv != '%') {
4106 pid_t pid = number(*argv);
4107 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004108 while (1) {
4109 if (!job)
4110 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004111 if (job->ps[job->nprocs - 1].pid == pid)
4112 break;
4113 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004114 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004115 } else
4116 job = getjob(*argv, 0);
4117 /* loop until process terminated or stopped */
4118 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004119 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004120 job->waited = 1;
4121 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004122 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004123 } while (*++argv);
4124
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004125 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004126 return retval;
4127}
4128
4129static struct job *
4130growjobtab(void)
4131{
4132 size_t len;
4133 ptrdiff_t offset;
4134 struct job *jp, *jq;
4135
4136 len = njobs * sizeof(*jp);
4137 jq = jobtab;
4138 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4139
4140 offset = (char *)jp - (char *)jq;
4141 if (offset) {
4142 /* Relocate pointers */
4143 size_t l = len;
4144
4145 jq = (struct job *)((char *)jq + l);
4146 while (l) {
4147 l -= sizeof(*jp);
4148 jq--;
4149#define joff(p) ((struct job *)((char *)(p) + l))
4150#define jmove(p) (p) = (void *)((char *)(p) + offset)
4151 if (joff(jp)->ps == &jq->ps0)
4152 jmove(joff(jp)->ps);
4153 if (joff(jp)->prev_job)
4154 jmove(joff(jp)->prev_job);
4155 }
4156 if (curjob)
4157 jmove(curjob);
4158#undef joff
4159#undef jmove
4160 }
4161
4162 njobs += 4;
4163 jobtab = jp;
4164 jp = (struct job *)((char *)jp + len);
4165 jq = jp + 3;
4166 do {
4167 jq->used = 0;
4168 } while (--jq >= jp);
4169 return jp;
4170}
4171
4172/*
4173 * Return a new job structure.
4174 * Called with interrupts off.
4175 */
4176static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004177makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004178{
4179 int i;
4180 struct job *jp;
4181
4182 for (i = njobs, jp = jobtab; ; jp++) {
4183 if (--i < 0) {
4184 jp = growjobtab();
4185 break;
4186 }
4187 if (jp->used == 0)
4188 break;
4189 if (jp->state != JOBDONE || !jp->waited)
4190 continue;
4191#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004192 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004193 continue;
4194#endif
4195 freejob(jp);
4196 break;
4197 }
4198 memset(jp, 0, sizeof(*jp));
4199#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004200 /* jp->jobctl is a bitfield.
4201 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004202 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 jp->jobctl = 1;
4204#endif
4205 jp->prev_job = curjob;
4206 curjob = jp;
4207 jp->used = 1;
4208 jp->ps = &jp->ps0;
4209 if (nprocs > 1) {
4210 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4211 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004212 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004213 jobno(jp)));
4214 return jp;
4215}
4216
4217#if JOBS
4218/*
4219 * Return a string identifying a command (to be printed by the
4220 * jobs command).
4221 */
4222static char *cmdnextc;
4223
4224static void
4225cmdputs(const char *s)
4226{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004227 static const char vstype[VSTYPE + 1][3] = {
4228 "", "}", "-", "+", "?", "=",
4229 "%", "%%", "#", "##"
4230 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4231 };
4232
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004233 const char *p, *str;
4234 char c, cc[2] = " ";
4235 char *nextc;
4236 int subtype = 0;
4237 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004238
4239 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4240 p = s;
4241 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004242 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004243 switch (c) {
4244 case CTLESC:
4245 c = *p++;
4246 break;
4247 case CTLVAR:
4248 subtype = *p++;
4249 if ((subtype & VSTYPE) == VSLENGTH)
4250 str = "${#";
4251 else
4252 str = "${";
4253 if (!(subtype & VSQUOTE) == !(quoted & 1))
4254 goto dostr;
4255 quoted ^= 1;
4256 c = '"';
4257 break;
4258 case CTLENDVAR:
4259 str = "\"}" + !(quoted & 1);
4260 quoted >>= 1;
4261 subtype = 0;
4262 goto dostr;
4263 case CTLBACKQ:
4264 str = "$(...)";
4265 goto dostr;
4266 case CTLBACKQ+CTLQUOTE:
4267 str = "\"$(...)\"";
4268 goto dostr;
4269#if ENABLE_ASH_MATH_SUPPORT
4270 case CTLARI:
4271 str = "$((";
4272 goto dostr;
4273 case CTLENDARI:
4274 str = "))";
4275 goto dostr;
4276#endif
4277 case CTLQUOTEMARK:
4278 quoted ^= 1;
4279 c = '"';
4280 break;
4281 case '=':
4282 if (subtype == 0)
4283 break;
4284 if ((subtype & VSTYPE) != VSNORMAL)
4285 quoted <<= 1;
4286 str = vstype[subtype & VSTYPE];
4287 if (subtype & VSNUL)
4288 c = ':';
4289 else
4290 goto checkstr;
4291 break;
4292 case '\'':
4293 case '\\':
4294 case '"':
4295 case '$':
4296 /* These can only happen inside quotes */
4297 cc[0] = c;
4298 str = cc;
4299 c = '\\';
4300 break;
4301 default:
4302 break;
4303 }
4304 USTPUTC(c, nextc);
4305 checkstr:
4306 if (!str)
4307 continue;
4308 dostr:
4309 while ((c = *str++)) {
4310 USTPUTC(c, nextc);
4311 }
4312 }
4313 if (quoted & 1) {
4314 USTPUTC('"', nextc);
4315 }
4316 *nextc = 0;
4317 cmdnextc = nextc;
4318}
4319
4320/* cmdtxt() and cmdlist() call each other */
4321static void cmdtxt(union node *n);
4322
4323static void
4324cmdlist(union node *np, int sep)
4325{
4326 for (; np; np = np->narg.next) {
4327 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004328 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004329 cmdtxt(np);
4330 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004331 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004332 }
4333}
4334
4335static void
4336cmdtxt(union node *n)
4337{
4338 union node *np;
4339 struct nodelist *lp;
4340 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004341
4342 if (!n)
4343 return;
4344 switch (n->type) {
4345 default:
4346#if DEBUG
4347 abort();
4348#endif
4349 case NPIPE:
4350 lp = n->npipe.cmdlist;
4351 for (;;) {
4352 cmdtxt(lp->n);
4353 lp = lp->next;
4354 if (!lp)
4355 break;
4356 cmdputs(" | ");
4357 }
4358 break;
4359 case NSEMI:
4360 p = "; ";
4361 goto binop;
4362 case NAND:
4363 p = " && ";
4364 goto binop;
4365 case NOR:
4366 p = " || ";
4367 binop:
4368 cmdtxt(n->nbinary.ch1);
4369 cmdputs(p);
4370 n = n->nbinary.ch2;
4371 goto donode;
4372 case NREDIR:
4373 case NBACKGND:
4374 n = n->nredir.n;
4375 goto donode;
4376 case NNOT:
4377 cmdputs("!");
4378 n = n->nnot.com;
4379 donode:
4380 cmdtxt(n);
4381 break;
4382 case NIF:
4383 cmdputs("if ");
4384 cmdtxt(n->nif.test);
4385 cmdputs("; then ");
4386 n = n->nif.ifpart;
4387 if (n->nif.elsepart) {
4388 cmdtxt(n);
4389 cmdputs("; else ");
4390 n = n->nif.elsepart;
4391 }
4392 p = "; fi";
4393 goto dotail;
4394 case NSUBSHELL:
4395 cmdputs("(");
4396 n = n->nredir.n;
4397 p = ")";
4398 goto dotail;
4399 case NWHILE:
4400 p = "while ";
4401 goto until;
4402 case NUNTIL:
4403 p = "until ";
4404 until:
4405 cmdputs(p);
4406 cmdtxt(n->nbinary.ch1);
4407 n = n->nbinary.ch2;
4408 p = "; done";
4409 dodo:
4410 cmdputs("; do ");
4411 dotail:
4412 cmdtxt(n);
4413 goto dotail2;
4414 case NFOR:
4415 cmdputs("for ");
4416 cmdputs(n->nfor.var);
4417 cmdputs(" in ");
4418 cmdlist(n->nfor.args, 1);
4419 n = n->nfor.body;
4420 p = "; done";
4421 goto dodo;
4422 case NDEFUN:
4423 cmdputs(n->narg.text);
4424 p = "() { ... }";
4425 goto dotail2;
4426 case NCMD:
4427 cmdlist(n->ncmd.args, 1);
4428 cmdlist(n->ncmd.redirect, 0);
4429 break;
4430 case NARG:
4431 p = n->narg.text;
4432 dotail2:
4433 cmdputs(p);
4434 break;
4435 case NHERE:
4436 case NXHERE:
4437 p = "<<...";
4438 goto dotail2;
4439 case NCASE:
4440 cmdputs("case ");
4441 cmdputs(n->ncase.expr->narg.text);
4442 cmdputs(" in ");
4443 for (np = n->ncase.cases; np; np = np->nclist.next) {
4444 cmdtxt(np->nclist.pattern);
4445 cmdputs(") ");
4446 cmdtxt(np->nclist.body);
4447 cmdputs(";; ");
4448 }
4449 p = "esac";
4450 goto dotail2;
4451 case NTO:
4452 p = ">";
4453 goto redir;
4454 case NCLOBBER:
4455 p = ">|";
4456 goto redir;
4457 case NAPPEND:
4458 p = ">>";
4459 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004460#if ENABLE_ASH_BASH_COMPAT
4461 case NTO2:
4462#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004463 case NTOFD:
4464 p = ">&";
4465 goto redir;
4466 case NFROM:
4467 p = "<";
4468 goto redir;
4469 case NFROMFD:
4470 p = "<&";
4471 goto redir;
4472 case NFROMTO:
4473 p = "<>";
4474 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004475 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004476 cmdputs(p);
4477 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004478 cmdputs(utoa(n->ndup.dupfd));
4479 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004480 }
4481 n = n->nfile.fname;
4482 goto donode;
4483 }
4484}
4485
4486static char *
4487commandtext(union node *n)
4488{
4489 char *name;
4490
4491 STARTSTACKSTR(cmdnextc);
4492 cmdtxt(n);
4493 name = stackblock();
4494 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4495 name, cmdnextc, cmdnextc));
4496 return ckstrdup(name);
4497}
4498#endif /* JOBS */
4499
4500/*
4501 * Fork off a subshell. If we are doing job control, give the subshell its
4502 * own process group. Jp is a job structure that the job is to be added to.
4503 * N is the command that will be evaluated by the child. Both jp and n may
4504 * be NULL. The mode parameter can be one of the following:
4505 * FORK_FG - Fork off a foreground process.
4506 * FORK_BG - Fork off a background process.
4507 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4508 * process group even if job control is on.
4509 *
4510 * When job control is turned off, background processes have their standard
4511 * input redirected to /dev/null (except for the second and later processes
4512 * in a pipeline).
4513 *
4514 * Called with interrupts off.
4515 */
4516/*
4517 * Clear traps on a fork.
4518 */
4519static void
4520clear_traps(void)
4521{
4522 char **tp;
4523
4524 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004525 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526 INT_OFF;
4527 free(*tp);
4528 *tp = NULL;
4529 if (tp != &trap[0])
4530 setsignal(tp - trap);
4531 INT_ON;
4532 }
4533 }
4534}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004535
4536/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004538
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004539/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004540static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004541forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004542{
4543 int oldlvl;
4544
4545 TRACE(("Child shell %d\n", getpid()));
4546 oldlvl = shlvl;
4547 shlvl++;
4548
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004549 /* man bash: "Non-builtin commands run by bash have signal handlers
4550 * set to the values inherited by the shell from its parent".
4551 * Do we do it correctly? */
4552
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004553 closescript();
4554 clear_traps();
4555#if JOBS
4556 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004557 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004558 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4559 pid_t pgrp;
4560
4561 if (jp->nprocs == 0)
4562 pgrp = getpid();
4563 else
4564 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004565 /* this can fail because we are doing it in the parent also */
4566 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004567 if (mode == FORK_FG)
4568 xtcsetpgrp(ttyfd, pgrp);
4569 setsignal(SIGTSTP);
4570 setsignal(SIGTTOU);
4571 } else
4572#endif
4573 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004574 /* man bash: "When job control is not in effect,
4575 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004576 ignoresig(SIGINT);
4577 ignoresig(SIGQUIT);
4578 if (jp->nprocs == 0) {
4579 close(0);
4580 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004581 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004582 }
4583 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004584 if (!oldlvl) {
4585 if (iflag) { /* why if iflag only? */
4586 setsignal(SIGINT);
4587 setsignal(SIGTERM);
4588 }
4589 /* man bash:
4590 * "In all cases, bash ignores SIGQUIT. Non-builtin
4591 * commands run by bash have signal handlers
4592 * set to the values inherited by the shell
4593 * from its parent".
4594 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004596 }
4597 for (jp = curjob; jp; jp = jp->prev_job)
4598 freejob(jp);
4599 jobless = 0;
4600}
4601
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004602/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004603#if !JOBS
4604#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4605#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004606static void
4607forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4608{
4609 TRACE(("In parent shell: child = %d\n", pid));
4610 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004611 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4612 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004613 jobless++;
4614 return;
4615 }
4616#if JOBS
4617 if (mode != FORK_NOJOB && jp->jobctl) {
4618 int pgrp;
4619
4620 if (jp->nprocs == 0)
4621 pgrp = pid;
4622 else
4623 pgrp = jp->ps[0].pid;
4624 /* This can fail because we are doing it in the child also */
4625 setpgid(pid, pgrp);
4626 }
4627#endif
4628 if (mode == FORK_BG) {
4629 backgndpid = pid; /* set $! */
4630 set_curjob(jp, CUR_RUNNING);
4631 }
4632 if (jp) {
4633 struct procstat *ps = &jp->ps[jp->nprocs++];
4634 ps->pid = pid;
4635 ps->status = -1;
4636 ps->cmd = nullstr;
4637#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004638 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004639 ps->cmd = commandtext(n);
4640#endif
4641 }
4642}
4643
4644static int
4645forkshell(struct job *jp, union node *n, int mode)
4646{
4647 int pid;
4648
4649 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4650 pid = fork();
4651 if (pid < 0) {
4652 TRACE(("Fork failed, errno=%d", errno));
4653 if (jp)
4654 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004655 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656 }
4657 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004658 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004659 else
4660 forkparent(jp, n, mode, pid);
4661 return pid;
4662}
4663
4664/*
4665 * Wait for job to finish.
4666 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004667 * Under job control we have the problem that while a child process
4668 * is running interrupts generated by the user are sent to the child
4669 * but not to the shell. This means that an infinite loop started by
4670 * an interactive user may be hard to kill. With job control turned off,
4671 * an interactive user may place an interactive program inside a loop.
4672 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004673 * these interrupts to also abort the loop. The approach we take here
4674 * is to have the shell ignore interrupt signals while waiting for a
4675 * foreground process to terminate, and then send itself an interrupt
4676 * signal if the child process was terminated by an interrupt signal.
4677 * Unfortunately, some programs want to do a bit of cleanup and then
4678 * exit on interrupt; unless these processes terminate themselves by
4679 * sending a signal to themselves (instead of calling exit) they will
4680 * confuse this approach.
4681 *
4682 * Called with interrupts off.
4683 */
4684static int
4685waitforjob(struct job *jp)
4686{
4687 int st;
4688
4689 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004690
4691 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004692 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004693 /* In non-interactive shells, we _can_ get
4694 * a keyboard signal here and be EINTRed,
4695 * but we just loop back, waiting for command to complete.
4696 *
4697 * man bash:
4698 * "If bash is waiting for a command to complete and receives
4699 * a signal for which a trap has been set, the trap
4700 * will not be executed until the command completes."
4701 *
4702 * Reality is that even if trap is not set, bash
4703 * will not act on the signal until command completes.
4704 * Try this. sleep5intoff.c:
4705 * #include <signal.h>
4706 * #include <unistd.h>
4707 * int main() {
4708 * sigset_t set;
4709 * sigemptyset(&set);
4710 * sigaddset(&set, SIGINT);
4711 * sigaddset(&set, SIGQUIT);
4712 * sigprocmask(SIG_BLOCK, &set, NULL);
4713 * sleep(5);
4714 * return 0;
4715 * }
4716 * $ bash -c './sleep5intoff; echo hi'
4717 * ^C^C^C^C <--- pressing ^C once a second
4718 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004719 * $ bash -c './sleep5intoff; echo hi'
4720 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4721 * $ _
4722 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004723 dowait(DOWAIT_BLOCK, jp);
4724 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004725 INT_ON;
4726
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004727 st = getstatus(jp);
4728#if JOBS
4729 if (jp->jobctl) {
4730 xtcsetpgrp(ttyfd, rootpid);
4731 /*
4732 * This is truly gross.
4733 * If we're doing job control, then we did a TIOCSPGRP which
4734 * caused us (the shell) to no longer be in the controlling
4735 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4736 * intuit from the subprocess exit status whether a SIGINT
4737 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4738 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004739 if (jp->sigint) /* TODO: do the same with all signals */
4740 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004741 }
4742 if (jp->state == JOBDONE)
4743#endif
4744 freejob(jp);
4745 return st;
4746}
4747
4748/*
4749 * return 1 if there are stopped jobs, otherwise 0
4750 */
4751static int
4752stoppedjobs(void)
4753{
4754 struct job *jp;
4755 int retval;
4756
4757 retval = 0;
4758 if (job_warning)
4759 goto out;
4760 jp = curjob;
4761 if (jp && jp->state == JOBSTOPPED) {
4762 out2str("You have stopped jobs.\n");
4763 job_warning = 2;
4764 retval++;
4765 }
4766 out:
4767 return retval;
4768}
4769
4770
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004771/* ============ redir.c
4772 *
4773 * Code for dealing with input/output redirection.
4774 */
4775
4776#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004777#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004778
4779/*
4780 * Open a file in noclobber mode.
4781 * The code was copied from bash.
4782 */
4783static int
4784noclobberopen(const char *fname)
4785{
4786 int r, fd;
4787 struct stat finfo, finfo2;
4788
4789 /*
4790 * If the file exists and is a regular file, return an error
4791 * immediately.
4792 */
4793 r = stat(fname, &finfo);
4794 if (r == 0 && S_ISREG(finfo.st_mode)) {
4795 errno = EEXIST;
4796 return -1;
4797 }
4798
4799 /*
4800 * If the file was not present (r != 0), make sure we open it
4801 * exclusively so that if it is created before we open it, our open
4802 * will fail. Make sure that we do not truncate an existing file.
4803 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4804 * file was not a regular file, we leave O_EXCL off.
4805 */
4806 if (r != 0)
4807 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4808 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4809
4810 /* If the open failed, return the file descriptor right away. */
4811 if (fd < 0)
4812 return fd;
4813
4814 /*
4815 * OK, the open succeeded, but the file may have been changed from a
4816 * non-regular file to a regular file between the stat and the open.
4817 * We are assuming that the O_EXCL open handles the case where FILENAME
4818 * did not exist and is symlinked to an existing file between the stat
4819 * and open.
4820 */
4821
4822 /*
4823 * If we can open it and fstat the file descriptor, and neither check
4824 * revealed that it was a regular file, and the file has not been
4825 * replaced, return the file descriptor.
4826 */
4827 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4828 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4829 return fd;
4830
4831 /* The file has been replaced. badness. */
4832 close(fd);
4833 errno = EEXIST;
4834 return -1;
4835}
4836
4837/*
4838 * Handle here documents. Normally we fork off a process to write the
4839 * data to a pipe. If the document is short, we can stuff the data in
4840 * the pipe without forking.
4841 */
4842/* openhere needs this forward reference */
4843static void expandhere(union node *arg, int fd);
4844static int
4845openhere(union node *redir)
4846{
4847 int pip[2];
4848 size_t len = 0;
4849
4850 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004851 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004852 if (redir->type == NHERE) {
4853 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004854 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004855 full_write(pip[1], redir->nhere.doc->narg.text, len);
4856 goto out;
4857 }
4858 }
4859 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004860 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004861 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004862 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4863 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4864 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4865 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004866 signal(SIGPIPE, SIG_DFL);
4867 if (redir->type == NHERE)
4868 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004869 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004870 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004871 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004872 }
4873 out:
4874 close(pip[1]);
4875 return pip[0];
4876}
4877
4878static int
4879openredirect(union node *redir)
4880{
4881 char *fname;
4882 int f;
4883
4884 switch (redir->nfile.type) {
4885 case NFROM:
4886 fname = redir->nfile.expfname;
4887 f = open(fname, O_RDONLY);
4888 if (f < 0)
4889 goto eopen;
4890 break;
4891 case NFROMTO:
4892 fname = redir->nfile.expfname;
4893 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4894 if (f < 0)
4895 goto ecreate;
4896 break;
4897 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004898#if ENABLE_ASH_BASH_COMPAT
4899 case NTO2:
4900#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004901 /* Take care of noclobber mode. */
4902 if (Cflag) {
4903 fname = redir->nfile.expfname;
4904 f = noclobberopen(fname);
4905 if (f < 0)
4906 goto ecreate;
4907 break;
4908 }
4909 /* FALLTHROUGH */
4910 case NCLOBBER:
4911 fname = redir->nfile.expfname;
4912 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4913 if (f < 0)
4914 goto ecreate;
4915 break;
4916 case NAPPEND:
4917 fname = redir->nfile.expfname;
4918 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4919 if (f < 0)
4920 goto ecreate;
4921 break;
4922 default:
4923#if DEBUG
4924 abort();
4925#endif
4926 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004927/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004928// case NTOFD:
4929// case NFROMFD:
4930// f = -1;
4931// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004932 case NHERE:
4933 case NXHERE:
4934 f = openhere(redir);
4935 break;
4936 }
4937
4938 return f;
4939 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004940 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004941 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004942 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004943}
4944
4945/*
4946 * Copy a file descriptor to be >= to. Returns -1
4947 * if the source file descriptor is closed, EMPTY if there are no unused
4948 * file descriptors left.
4949 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004950/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4951 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004952enum {
4953 COPYFD_EXACT = (int)~(INT_MAX),
4954 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4955};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004956static int
4957copyfd(int from, int to)
4958{
4959 int newfd;
4960
Denis Vlasenko5a867312008-07-24 19:46:38 +00004961 if (to & COPYFD_EXACT) {
4962 to &= ~COPYFD_EXACT;
4963 /*if (from != to)*/
4964 newfd = dup2(from, to);
4965 } else {
4966 newfd = fcntl(from, F_DUPFD, to);
4967 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004968 if (newfd < 0) {
4969 if (errno == EMFILE)
4970 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004971 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004972 ash_msg_and_raise_error("%d: %m", from);
4973 }
4974 return newfd;
4975}
4976
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004977/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004978struct two_fd_t {
4979 int orig, copy;
4980};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004981struct redirtab {
4982 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004983 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004984 int pair_count;
4985 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004986};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004987#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004988
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004989static int need_to_remember(struct redirtab *rp, int fd)
4990{
4991 int i;
4992
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004993 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004994 return 0;
4995
4996 for (i = 0; i < rp->pair_count; i++) {
4997 if (rp->two_fd[i].orig == fd) {
4998 /* already remembered */
4999 return 0;
5000 }
5001 }
5002 return 1;
5003}
5004
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005005/* "hidden" fd is a fd used to read scripts, or a copy of such */
5006static int is_hidden_fd(struct redirtab *rp, int fd)
5007{
5008 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005009 struct parsefile *pf;
5010
5011 if (fd == -1)
5012 return 0;
5013 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005014 while (pf) {
5015 if (fd == pf->fd) {
5016 return 1;
5017 }
5018 pf = pf->prev;
5019 }
5020 if (!rp)
5021 return 0;
5022 fd |= COPYFD_RESTORE;
5023 for (i = 0; i < rp->pair_count; i++) {
5024 if (rp->two_fd[i].copy == fd) {
5025 return 1;
5026 }
5027 }
5028 return 0;
5029}
5030
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005031/*
5032 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5033 * old file descriptors are stashed away so that the redirection can be
5034 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
5035 * standard output, and the standard error if it becomes a duplicate of
5036 * stdout, is saved in memory.
5037 */
5038/* flags passed to redirect */
5039#define REDIR_PUSH 01 /* save previous values of file descriptors */
5040#define REDIR_SAVEFD2 03 /* set preverrout */
5041static void
5042redirect(union node *redir, int flags)
5043{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005044 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005045 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005046 int i;
5047 int fd;
5048 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005049 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005050
Denis Vlasenko01631112007-12-16 17:20:38 +00005051 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005052 if (!redir) {
5053 return;
5054 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005055
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005056 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005057 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005058 INT_OFF;
5059 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005060 union node *tmp = redir;
5061 do {
5062 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005063#if ENABLE_ASH_BASH_COMPAT
5064 if (redir->nfile.type == NTO2)
5065 sv_pos++;
5066#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005067 tmp = tmp->nfile.next;
5068 } while (tmp);
5069 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005070 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005071 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005072 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005073 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005074 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005075 while (sv_pos > 0) {
5076 sv_pos--;
5077 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5078 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005080
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005081 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005082 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005083 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005084 int right_fd = redir->ndup.dupfd;
5085 /* redirect from/to same file descriptor? */
5086 if (right_fd == fd)
5087 continue;
5088 /* echo >&10 and 10 is a fd opened to the sh script? */
5089 if (is_hidden_fd(sv, right_fd)) {
5090 errno = EBADF; /* as if it is closed */
5091 ash_msg_and_raise_error("%d: %m", right_fd);
5092 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005093 newfd = -1;
5094 } else {
5095 newfd = openredirect(redir); /* always >= 0 */
5096 if (fd == newfd) {
5097 /* Descriptor wasn't open before redirect.
5098 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005099 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005100 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005101 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005102 continue;
5103 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005104 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005105#if ENABLE_ASH_BASH_COMPAT
5106 redirect_more:
5107#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005108 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005109 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005110 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005111/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5112 * are closed in popredir() in the child, preventing them from leaking
5113 * into child. (popredir() also cleans up the mess in case of failures)
5114 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005115 if (i == -1) {
5116 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005117 if (i != EBADF) {
5118 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005119 if (newfd >= 0)
5120 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 errno = i;
5122 ash_msg_and_raise_error("%d: %m", fd);
5123 /* NOTREACHED */
5124 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005125 /* EBADF: it is not open - good, remember to close it */
5126 remember_to_close:
5127 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005128 } else { /* fd is open, save its copy */
5129 /* "exec fd>&-" should not close fds
5130 * which point to script file(s).
5131 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005132 if (is_hidden_fd(sv, fd))
5133 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005134 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005135 if (fd == 2)
5136 copied_fd2 = i;
5137 sv->two_fd[sv_pos].orig = fd;
5138 sv->two_fd[sv_pos].copy = i;
5139 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005140 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005141 if (newfd < 0) {
5142 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005143 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005144 close(fd);
5145 } else {
5146 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005147 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005148 } else if (fd != newfd) { /* move newfd to fd */
5149 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005150#if ENABLE_ASH_BASH_COMPAT
5151 if (!(redir->nfile.type == NTO2 && fd == 2))
5152#endif
5153 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005154 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005155#if ENABLE_ASH_BASH_COMPAT
5156 if (redir->nfile.type == NTO2 && fd == 1) {
5157 /* We already redirected it to fd 1, now copy it to 2 */
5158 newfd = 1;
5159 fd = 2;
5160 goto redirect_more;
5161 }
5162#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005163 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005164
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005165 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005166 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5167 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005168}
5169
5170/*
5171 * Undo the effects of the last redirection.
5172 */
5173static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005174popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005175{
5176 struct redirtab *rp;
5177 int i;
5178
Denis Vlasenko01631112007-12-16 17:20:38 +00005179 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005180 return;
5181 INT_OFF;
5182 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005183 for (i = 0; i < rp->pair_count; i++) {
5184 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005185 int copy = rp->two_fd[i].copy;
5186 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005187 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005188 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005189 continue;
5190 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005191 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005192 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005193 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005194 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005195 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005196 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005197 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005198 }
5199 }
5200 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005201 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005202 free(rp);
5203 INT_ON;
5204}
5205
5206/*
5207 * Undo all redirections. Called on error or interrupt.
5208 */
5209
5210/*
5211 * Discard all saved file descriptors.
5212 */
5213static void
5214clearredir(int drop)
5215{
5216 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005217 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005218 if (!redirlist)
5219 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005220 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005221 }
5222}
5223
5224static int
5225redirectsafe(union node *redir, int flags)
5226{
5227 int err;
5228 volatile int saveint;
5229 struct jmploc *volatile savehandler = exception_handler;
5230 struct jmploc jmploc;
5231
5232 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005233 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5234 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005235 if (!err) {
5236 exception_handler = &jmploc;
5237 redirect(redir, flags);
5238 }
5239 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005240 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005241 longjmp(exception_handler->loc, 1);
5242 RESTORE_INT(saveint);
5243 return err;
5244}
5245
5246
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005247/* ============ Routines to expand arguments to commands
5248 *
5249 * We have to deal with backquotes, shell variables, and file metacharacters.
5250 */
5251
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005252#if ENABLE_ASH_MATH_SUPPORT_64
5253typedef int64_t arith_t;
5254#define arith_t_type long long
5255#else
5256typedef long arith_t;
5257#define arith_t_type long
5258#endif
5259
5260#if ENABLE_ASH_MATH_SUPPORT
5261static arith_t dash_arith(const char *);
5262static arith_t arith(const char *expr, int *perrcode);
5263#endif
5264
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005265/*
5266 * expandarg flags
5267 */
5268#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5269#define EXP_TILDE 0x2 /* do normal tilde expansion */
5270#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5271#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5272#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5273#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5274#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5275#define EXP_WORD 0x80 /* expand word in parameter expansion */
5276#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5277/*
5278 * _rmescape() flags
5279 */
5280#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5281#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5282#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5283#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5284#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5285
5286/*
5287 * Structure specifying which parts of the string should be searched
5288 * for IFS characters.
5289 */
5290struct ifsregion {
5291 struct ifsregion *next; /* next region in list */
5292 int begoff; /* offset of start of region */
5293 int endoff; /* offset of end of region */
5294 int nulonly; /* search for nul bytes only */
5295};
5296
5297struct arglist {
5298 struct strlist *list;
5299 struct strlist **lastp;
5300};
5301
5302/* output of current string */
5303static char *expdest;
5304/* list of back quote expressions */
5305static struct nodelist *argbackq;
5306/* first struct in list of ifs regions */
5307static struct ifsregion ifsfirst;
5308/* last struct in list */
5309static struct ifsregion *ifslastp;
5310/* holds expanded arg list */
5311static struct arglist exparg;
5312
5313/*
5314 * Our own itoa().
5315 */
5316static int
5317cvtnum(arith_t num)
5318{
5319 int len;
5320
5321 expdest = makestrspace(32, expdest);
5322#if ENABLE_ASH_MATH_SUPPORT_64
5323 len = fmtstr(expdest, 32, "%lld", (long long) num);
5324#else
5325 len = fmtstr(expdest, 32, "%ld", num);
5326#endif
5327 STADJUST(len, expdest);
5328 return len;
5329}
5330
5331static size_t
5332esclen(const char *start, const char *p)
5333{
5334 size_t esc = 0;
5335
5336 while (p > start && *--p == CTLESC) {
5337 esc++;
5338 }
5339 return esc;
5340}
5341
5342/*
5343 * Remove any CTLESC characters from a string.
5344 */
5345static char *
5346_rmescapes(char *str, int flag)
5347{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005348 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005349
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005350 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005351 unsigned inquotes;
5352 int notescaped;
5353 int globbing;
5354
5355 p = strpbrk(str, qchars);
5356 if (!p) {
5357 return str;
5358 }
5359 q = p;
5360 r = str;
5361 if (flag & RMESCAPE_ALLOC) {
5362 size_t len = p - str;
5363 size_t fulllen = len + strlen(p) + 1;
5364
5365 if (flag & RMESCAPE_GROW) {
5366 r = makestrspace(fulllen, expdest);
5367 } else if (flag & RMESCAPE_HEAP) {
5368 r = ckmalloc(fulllen);
5369 } else {
5370 r = stalloc(fulllen);
5371 }
5372 q = r;
5373 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005374 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005375 }
5376 }
5377 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5378 globbing = flag & RMESCAPE_GLOB;
5379 notescaped = globbing;
5380 while (*p) {
5381 if (*p == CTLQUOTEMARK) {
5382 inquotes = ~inquotes;
5383 p++;
5384 notescaped = globbing;
5385 continue;
5386 }
5387 if (*p == '\\') {
5388 /* naked back slash */
5389 notescaped = 0;
5390 goto copy;
5391 }
5392 if (*p == CTLESC) {
5393 p++;
5394 if (notescaped && inquotes && *p != '/') {
5395 *q++ = '\\';
5396 }
5397 }
5398 notescaped = globbing;
5399 copy:
5400 *q++ = *p++;
5401 }
5402 *q = '\0';
5403 if (flag & RMESCAPE_GROW) {
5404 expdest = r;
5405 STADJUST(q - r + 1, expdest);
5406 }
5407 return r;
5408}
5409#define rmescapes(p) _rmescapes((p), 0)
5410
5411#define pmatch(a, b) !fnmatch((a), (b), 0)
5412
5413/*
5414 * Prepare a pattern for a expmeta (internal glob(3)) call.
5415 *
5416 * Returns an stalloced string.
5417 */
5418static char *
5419preglob(const char *pattern, int quoted, int flag)
5420{
5421 flag |= RMESCAPE_GLOB;
5422 if (quoted) {
5423 flag |= RMESCAPE_QUOTED;
5424 }
5425 return _rmescapes((char *)pattern, flag);
5426}
5427
5428/*
5429 * Put a string on the stack.
5430 */
5431static void
5432memtodest(const char *p, size_t len, int syntax, int quotes)
5433{
5434 char *q = expdest;
5435
5436 q = makestrspace(len * 2, q);
5437
5438 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005439 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005440 if (!c)
5441 continue;
5442 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5443 USTPUTC(CTLESC, q);
5444 USTPUTC(c, q);
5445 }
5446
5447 expdest = q;
5448}
5449
5450static void
5451strtodest(const char *p, int syntax, int quotes)
5452{
5453 memtodest(p, strlen(p), syntax, quotes);
5454}
5455
5456/*
5457 * Record the fact that we have to scan this region of the
5458 * string for IFS characters.
5459 */
5460static void
5461recordregion(int start, int end, int nulonly)
5462{
5463 struct ifsregion *ifsp;
5464
5465 if (ifslastp == NULL) {
5466 ifsp = &ifsfirst;
5467 } else {
5468 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005469 ifsp = ckzalloc(sizeof(*ifsp));
5470 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005471 ifslastp->next = ifsp;
5472 INT_ON;
5473 }
5474 ifslastp = ifsp;
5475 ifslastp->begoff = start;
5476 ifslastp->endoff = end;
5477 ifslastp->nulonly = nulonly;
5478}
5479
5480static void
5481removerecordregions(int endoff)
5482{
5483 if (ifslastp == NULL)
5484 return;
5485
5486 if (ifsfirst.endoff > endoff) {
5487 while (ifsfirst.next != NULL) {
5488 struct ifsregion *ifsp;
5489 INT_OFF;
5490 ifsp = ifsfirst.next->next;
5491 free(ifsfirst.next);
5492 ifsfirst.next = ifsp;
5493 INT_ON;
5494 }
5495 if (ifsfirst.begoff > endoff)
5496 ifslastp = NULL;
5497 else {
5498 ifslastp = &ifsfirst;
5499 ifsfirst.endoff = endoff;
5500 }
5501 return;
5502 }
5503
5504 ifslastp = &ifsfirst;
5505 while (ifslastp->next && ifslastp->next->begoff < endoff)
5506 ifslastp=ifslastp->next;
5507 while (ifslastp->next != NULL) {
5508 struct ifsregion *ifsp;
5509 INT_OFF;
5510 ifsp = ifslastp->next->next;
5511 free(ifslastp->next);
5512 ifslastp->next = ifsp;
5513 INT_ON;
5514 }
5515 if (ifslastp->endoff > endoff)
5516 ifslastp->endoff = endoff;
5517}
5518
5519static char *
5520exptilde(char *startp, char *p, int flag)
5521{
5522 char c;
5523 char *name;
5524 struct passwd *pw;
5525 const char *home;
5526 int quotes = flag & (EXP_FULL | EXP_CASE);
5527 int startloc;
5528
5529 name = p + 1;
5530
5531 while ((c = *++p) != '\0') {
5532 switch (c) {
5533 case CTLESC:
5534 return startp;
5535 case CTLQUOTEMARK:
5536 return startp;
5537 case ':':
5538 if (flag & EXP_VARTILDE)
5539 goto done;
5540 break;
5541 case '/':
5542 case CTLENDVAR:
5543 goto done;
5544 }
5545 }
5546 done:
5547 *p = '\0';
5548 if (*name == '\0') {
5549 home = lookupvar(homestr);
5550 } else {
5551 pw = getpwnam(name);
5552 if (pw == NULL)
5553 goto lose;
5554 home = pw->pw_dir;
5555 }
5556 if (!home || !*home)
5557 goto lose;
5558 *p = c;
5559 startloc = expdest - (char *)stackblock();
5560 strtodest(home, SQSYNTAX, quotes);
5561 recordregion(startloc, expdest - (char *)stackblock(), 0);
5562 return p;
5563 lose:
5564 *p = c;
5565 return startp;
5566}
5567
5568/*
5569 * Execute a command inside back quotes. If it's a builtin command, we
5570 * want to save its output in a block obtained from malloc. Otherwise
5571 * we fork off a subprocess and get the output of the command via a pipe.
5572 * Should be called with interrupts off.
5573 */
5574struct backcmd { /* result of evalbackcmd */
5575 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005577 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005578 struct job *jp; /* job structure for command */
5579};
5580
5581/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005582static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005583#define EV_EXIT 01 /* exit after evaluating tree */
5584static void evaltree(union node *, int);
5585
5586static void
5587evalbackcmd(union node *n, struct backcmd *result)
5588{
5589 int saveherefd;
5590
5591 result->fd = -1;
5592 result->buf = NULL;
5593 result->nleft = 0;
5594 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005595 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005597
5598 saveherefd = herefd;
5599 herefd = -1;
5600
5601 {
5602 int pip[2];
5603 struct job *jp;
5604
5605 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005606 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005607 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005608 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5609 FORCE_INT_ON;
5610 close(pip[0]);
5611 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005612 /*close(1);*/
5613 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005614 close(pip[1]);
5615 }
5616 eflag = 0;
5617 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5618 /* NOTREACHED */
5619 }
5620 close(pip[1]);
5621 result->fd = pip[0];
5622 result->jp = jp;
5623 }
5624 herefd = saveherefd;
5625 out:
5626 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5627 result->fd, result->buf, result->nleft, result->jp));
5628}
5629
5630/*
5631 * Expand stuff in backwards quotes.
5632 */
5633static void
5634expbackq(union node *cmd, int quoted, int quotes)
5635{
5636 struct backcmd in;
5637 int i;
5638 char buf[128];
5639 char *p;
5640 char *dest;
5641 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005642 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005643 struct stackmark smark;
5644
5645 INT_OFF;
5646 setstackmark(&smark);
5647 dest = expdest;
5648 startloc = dest - (char *)stackblock();
5649 grabstackstr(dest);
5650 evalbackcmd(cmd, &in);
5651 popstackmark(&smark);
5652
5653 p = in.buf;
5654 i = in.nleft;
5655 if (i == 0)
5656 goto read;
5657 for (;;) {
5658 memtodest(p, i, syntax, quotes);
5659 read:
5660 if (in.fd < 0)
5661 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005662 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005663 TRACE(("expbackq: read returns %d\n", i));
5664 if (i <= 0)
5665 break;
5666 p = buf;
5667 }
5668
Denis Vlasenko60818682007-09-28 22:07:23 +00005669 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005670 if (in.fd >= 0) {
5671 close(in.fd);
5672 back_exitstatus = waitforjob(in.jp);
5673 }
5674 INT_ON;
5675
5676 /* Eat all trailing newlines */
5677 dest = expdest;
5678 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5679 STUNPUTC(dest);
5680 expdest = dest;
5681
5682 if (quoted == 0)
5683 recordregion(startloc, dest - (char *)stackblock(), 0);
5684 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5685 (dest - (char *)stackblock()) - startloc,
5686 (dest - (char *)stackblock()) - startloc,
5687 stackblock() + startloc));
5688}
5689
5690#if ENABLE_ASH_MATH_SUPPORT
5691/*
5692 * Expand arithmetic expression. Backup to start of expression,
5693 * evaluate, place result in (backed up) result, adjust string position.
5694 */
5695static void
5696expari(int quotes)
5697{
5698 char *p, *start;
5699 int begoff;
5700 int flag;
5701 int len;
5702
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005703 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005704
5705 /*
5706 * This routine is slightly over-complicated for
5707 * efficiency. Next we scan backwards looking for the
5708 * start of arithmetic.
5709 */
5710 start = stackblock();
5711 p = expdest - 1;
5712 *p = '\0';
5713 p--;
5714 do {
5715 int esc;
5716
5717 while (*p != CTLARI) {
5718 p--;
5719#if DEBUG
5720 if (p < start) {
5721 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5722 }
5723#endif
5724 }
5725
5726 esc = esclen(start, p);
5727 if (!(esc % 2)) {
5728 break;
5729 }
5730
5731 p -= esc + 1;
5732 } while (1);
5733
5734 begoff = p - start;
5735
5736 removerecordregions(begoff);
5737
5738 flag = p[1];
5739
5740 expdest = p;
5741
5742 if (quotes)
5743 rmescapes(p + 2);
5744
5745 len = cvtnum(dash_arith(p + 2));
5746
5747 if (flag != '"')
5748 recordregion(begoff, begoff + len, 0);
5749}
5750#endif
5751
5752/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005753static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005754
5755/*
5756 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5757 * characters to allow for further processing. Otherwise treat
5758 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005759 *
5760 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5761 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5762 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005763 */
5764static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005765argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005766{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005767 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005768 '=',
5769 ':',
5770 CTLQUOTEMARK,
5771 CTLENDVAR,
5772 CTLESC,
5773 CTLVAR,
5774 CTLBACKQ,
5775 CTLBACKQ | CTLQUOTE,
5776#if ENABLE_ASH_MATH_SUPPORT
5777 CTLENDARI,
5778#endif
5779 0
5780 };
5781 const char *reject = spclchars;
5782 int c;
5783 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5784 int breakall = flag & EXP_WORD;
5785 int inquotes;
5786 size_t length;
5787 int startloc;
5788
5789 if (!(flag & EXP_VARTILDE)) {
5790 reject += 2;
5791 } else if (flag & EXP_VARTILDE2) {
5792 reject++;
5793 }
5794 inquotes = 0;
5795 length = 0;
5796 if (flag & EXP_TILDE) {
5797 char *q;
5798
5799 flag &= ~EXP_TILDE;
5800 tilde:
5801 q = p;
5802 if (*q == CTLESC && (flag & EXP_QWORD))
5803 q++;
5804 if (*q == '~')
5805 p = exptilde(p, q, flag);
5806 }
5807 start:
5808 startloc = expdest - (char *)stackblock();
5809 for (;;) {
5810 length += strcspn(p + length, reject);
5811 c = p[length];
5812 if (c && (!(c & 0x80)
5813#if ENABLE_ASH_MATH_SUPPORT
5814 || c == CTLENDARI
5815#endif
5816 )) {
5817 /* c == '=' || c == ':' || c == CTLENDARI */
5818 length++;
5819 }
5820 if (length > 0) {
5821 int newloc;
5822 expdest = stack_nputstr(p, length, expdest);
5823 newloc = expdest - (char *)stackblock();
5824 if (breakall && !inquotes && newloc > startloc) {
5825 recordregion(startloc, newloc, 0);
5826 }
5827 startloc = newloc;
5828 }
5829 p += length + 1;
5830 length = 0;
5831
5832 switch (c) {
5833 case '\0':
5834 goto breakloop;
5835 case '=':
5836 if (flag & EXP_VARTILDE2) {
5837 p--;
5838 continue;
5839 }
5840 flag |= EXP_VARTILDE2;
5841 reject++;
5842 /* fall through */
5843 case ':':
5844 /*
5845 * sort of a hack - expand tildes in variable
5846 * assignments (after the first '=' and after ':'s).
5847 */
5848 if (*--p == '~') {
5849 goto tilde;
5850 }
5851 continue;
5852 }
5853
5854 switch (c) {
5855 case CTLENDVAR: /* ??? */
5856 goto breakloop;
5857 case CTLQUOTEMARK:
5858 /* "$@" syntax adherence hack */
5859 if (
5860 !inquotes &&
5861 !memcmp(p, dolatstr, 4) &&
5862 (p[4] == CTLQUOTEMARK || (
5863 p[4] == CTLENDVAR &&
5864 p[5] == CTLQUOTEMARK
5865 ))
5866 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005867 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 goto start;
5869 }
5870 inquotes = !inquotes;
5871 addquote:
5872 if (quotes) {
5873 p--;
5874 length++;
5875 startloc++;
5876 }
5877 break;
5878 case CTLESC:
5879 startloc++;
5880 length++;
5881 goto addquote;
5882 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005883 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005884 goto start;
5885 case CTLBACKQ:
5886 c = 0;
5887 case CTLBACKQ|CTLQUOTE:
5888 expbackq(argbackq->n, c, quotes);
5889 argbackq = argbackq->next;
5890 goto start;
5891#if ENABLE_ASH_MATH_SUPPORT
5892 case CTLENDARI:
5893 p--;
5894 expari(quotes);
5895 goto start;
5896#endif
5897 }
5898 }
5899 breakloop:
5900 ;
5901}
5902
5903static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005904scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005905 int zero)
5906{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005907// This commented out code was added by James Simmons <jsimmons@infradead.org>
5908// as part of a larger change when he added support for ${var/a/b}.
5909// However, it broke # and % operators:
5910//
5911//var=ababcdcd
5912// ok bad
5913//echo ${var#ab} abcdcd abcdcd
5914//echo ${var##ab} abcdcd abcdcd
5915//echo ${var#a*b} abcdcd ababcdcd (!)
5916//echo ${var##a*b} cdcd cdcd
5917//echo ${var#?} babcdcd ababcdcd (!)
5918//echo ${var##?} babcdcd babcdcd
5919//echo ${var#*} ababcdcd babcdcd (!)
5920//echo ${var##*}
5921//echo ${var%cd} ababcd ababcd
5922//echo ${var%%cd} ababcd abab (!)
5923//echo ${var%c*d} ababcd ababcd
5924//echo ${var%%c*d} abab ababcdcd (!)
5925//echo ${var%?} ababcdc ababcdc
5926//echo ${var%%?} ababcdc ababcdcd (!)
5927//echo ${var%*} ababcdcd ababcdcd
5928//echo ${var%%*}
5929//
5930// Commenting it back out helped. Remove it completely if it really
5931// is not needed.
5932
5933 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005934 char c;
5935
5936 loc = startp;
5937 loc2 = rmesc;
5938 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005939 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005940 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005941
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005942 c = *loc2;
5943 if (zero) {
5944 *loc2 = '\0';
5945 s = rmesc;
5946 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005947 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005948
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005949// // chop off end if its '*'
5950// full = strrchr(str, '*');
5951// if (full && full != str)
5952// match--;
5953//
5954// // If str starts with '*' replace with s.
5955// if ((*str == '*') && strlen(s) >= match) {
5956// full = xstrdup(s);
5957// strncpy(full+strlen(s)-match+1, str+1, match-1);
5958// } else
5959// full = xstrndup(str, match);
5960// match = strncmp(s, full, strlen(full));
5961// free(full);
5962//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005964 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005965 return loc;
5966 if (quotes && *loc == CTLESC)
5967 loc++;
5968 loc++;
5969 loc2++;
5970 } while (c);
5971 return 0;
5972}
5973
5974static char *
5975scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5976 int zero)
5977{
5978 int esc = 0;
5979 char *loc;
5980 char *loc2;
5981
5982 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5983 int match;
5984 char c = *loc2;
5985 const char *s = loc2;
5986 if (zero) {
5987 *loc2 = '\0';
5988 s = rmesc;
5989 }
5990 match = pmatch(str, s);
5991 *loc2 = c;
5992 if (match)
5993 return loc;
5994 loc--;
5995 if (quotes) {
5996 if (--esc < 0) {
5997 esc = esclen(startp, loc);
5998 }
5999 if (esc % 2) {
6000 esc--;
6001 loc--;
6002 }
6003 }
6004 }
6005 return 0;
6006}
6007
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006008static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006009static void
6010varunset(const char *end, const char *var, const char *umsg, int varflags)
6011{
6012 const char *msg;
6013 const char *tail;
6014
6015 tail = nullstr;
6016 msg = "parameter not set";
6017 if (umsg) {
6018 if (*end == CTLENDVAR) {
6019 if (varflags & VSNUL)
6020 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006021 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006022 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006023 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006024 }
6025 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6026}
6027
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006028#if ENABLE_ASH_BASH_COMPAT
6029static char *
6030parse_sub_pattern(char *arg, int inquotes)
6031{
6032 char *idx, *repl = NULL;
6033 unsigned char c;
6034
Denis Vlasenko2659c632008-06-14 06:04:59 +00006035 idx = arg;
6036 while (1) {
6037 c = *arg;
6038 if (!c)
6039 break;
6040 if (c == '/') {
6041 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006042 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006043 repl = idx + 1;
6044 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006045 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006046 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006047 *idx++ = c;
6048 if (!inquotes && c == '\\' && arg[1] == '\\')
6049 arg++; /* skip both \\, not just first one */
6050 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006051 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006052 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006053
6054 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006055}
6056#endif /* ENABLE_ASH_BASH_COMPAT */
6057
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006058static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006059subevalvar(char *p, char *str, int strloc, int subtype,
6060 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006062 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 char *startp;
6064 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006066 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6067 USE_ASH_BASH_COMPAT(char null = '\0';)
6068 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6069 int saveherefd = herefd;
6070 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006072 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006073
6074 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006075 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6076 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006077 STPUTC('\0', expdest);
6078 herefd = saveherefd;
6079 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006080 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006081
6082 switch (subtype) {
6083 case VSASSIGN:
6084 setvar(str, startp, 0);
6085 amount = startp - expdest;
6086 STADJUST(amount, expdest);
6087 return startp;
6088
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006089#if ENABLE_ASH_BASH_COMPAT
6090 case VSSUBSTR:
6091 loc = str = stackblock() + strloc;
6092// TODO: number() instead? It does error checking...
6093 pos = atoi(loc);
6094 len = str - startp - 1;
6095
6096 /* *loc != '\0', guaranteed by parser */
6097 if (quotes) {
6098 char *ptr;
6099
6100 /* We must adjust the length by the number of escapes we find. */
6101 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006102 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006103 len--;
6104 ptr++;
6105 }
6106 }
6107 }
6108 orig_len = len;
6109
6110 if (*loc++ == ':') {
6111// TODO: number() instead? It does error checking...
6112 len = atoi(loc);
6113 } else {
6114 len = orig_len;
6115 while (*loc && *loc != ':')
6116 loc++;
6117 if (*loc++ == ':')
6118// TODO: number() instead? It does error checking...
6119 len = atoi(loc);
6120 }
6121 if (pos >= orig_len) {
6122 pos = 0;
6123 len = 0;
6124 }
6125 if (len > (orig_len - pos))
6126 len = orig_len - pos;
6127
6128 for (str = startp; pos; str++, pos--) {
6129 if (quotes && *str == CTLESC)
6130 str++;
6131 }
6132 for (loc = startp; len; len--) {
6133 if (quotes && *str == CTLESC)
6134 *loc++ = *str++;
6135 *loc++ = *str++;
6136 }
6137 *loc = '\0';
6138 amount = loc - expdest;
6139 STADJUST(amount, expdest);
6140 return loc;
6141#endif
6142
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006143 case VSQUESTION:
6144 varunset(p, str, startp, varflags);
6145 /* NOTREACHED */
6146 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006147 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006148
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006149 /* We'll comeback here if we grow the stack while handling
6150 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6151 * stack will need rebasing, and we'll need to remove our work
6152 * areas each time
6153 */
6154 USE_ASH_BASH_COMPAT(restart:)
6155
6156 amount = expdest - ((char *)stackblock() + resetloc);
6157 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006158 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006159
6160 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006161 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006162 if (quotes) {
6163 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6164 if (rmesc != startp) {
6165 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006166 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006167 }
6168 }
6169 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006170 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006171 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006172 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006173
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006174#if ENABLE_ASH_BASH_COMPAT
6175 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6176 char *idx, *end, *restart_detect;
6177
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006178 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006179 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6180 if (!repl)
6181 repl = &null;
6182 }
6183
6184 /* If there's no pattern to match, return the expansion unmolested */
6185 if (*str == '\0')
6186 return 0;
6187
6188 len = 0;
6189 idx = startp;
6190 end = str - 1;
6191 while (idx < end) {
6192 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6193 if (!loc) {
6194 /* No match, advance */
6195 restart_detect = stackblock();
6196 STPUTC(*idx, expdest);
6197 if (quotes && *idx == CTLESC) {
6198 idx++;
6199 len++;
6200 STPUTC(*idx, expdest);
6201 }
6202 if (stackblock() != restart_detect)
6203 goto restart;
6204 idx++;
6205 len++;
6206 rmesc++;
6207 continue;
6208 }
6209
6210 if (subtype == VSREPLACEALL) {
6211 while (idx < loc) {
6212 if (quotes && *idx == CTLESC)
6213 idx++;
6214 idx++;
6215 rmesc++;
6216 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006217 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006218 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006219 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006220
6221 for (loc = repl; *loc; loc++) {
6222 restart_detect = stackblock();
6223 STPUTC(*loc, expdest);
6224 if (stackblock() != restart_detect)
6225 goto restart;
6226 len++;
6227 }
6228
6229 if (subtype == VSREPLACE) {
6230 while (*idx) {
6231 restart_detect = stackblock();
6232 STPUTC(*idx, expdest);
6233 if (stackblock() != restart_detect)
6234 goto restart;
6235 len++;
6236 idx++;
6237 }
6238 break;
6239 }
6240 }
6241
6242 /* We've put the replaced text into a buffer at workloc, now
6243 * move it to the right place and adjust the stack.
6244 */
6245 startp = stackblock() + startloc;
6246 STPUTC('\0', expdest);
6247 memmove(startp, stackblock() + workloc, len);
6248 startp[len++] = '\0';
6249 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6250 STADJUST(-amount, expdest);
6251 return startp;
6252 }
6253#endif /* ENABLE_ASH_BASH_COMPAT */
6254
6255 subtype -= VSTRIMRIGHT;
6256#if DEBUG
6257 if (subtype < 0 || subtype > 7)
6258 abort();
6259#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006260 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6261 zero = subtype >> 1;
6262 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6263 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6264
6265 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6266 if (loc) {
6267 if (zero) {
6268 memmove(startp, loc, str - loc);
6269 loc = startp + (str - loc) - 1;
6270 }
6271 *loc = '\0';
6272 amount = loc - expdest;
6273 STADJUST(amount, expdest);
6274 }
6275 return loc;
6276}
6277
6278/*
6279 * Add the value of a specialized variable to the stack string.
6280 */
6281static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006282varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006283{
6284 int num;
6285 char *p;
6286 int i;
6287 int sep = 0;
6288 int sepq = 0;
6289 ssize_t len = 0;
6290 char **ap;
6291 int syntax;
6292 int quoted = varflags & VSQUOTE;
6293 int subtype = varflags & VSTYPE;
6294 int quotes = flags & (EXP_FULL | EXP_CASE);
6295
6296 if (quoted && (flags & EXP_FULL))
6297 sep = 1 << CHAR_BIT;
6298
6299 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6300 switch (*name) {
6301 case '$':
6302 num = rootpid;
6303 goto numvar;
6304 case '?':
6305 num = exitstatus;
6306 goto numvar;
6307 case '#':
6308 num = shellparam.nparam;
6309 goto numvar;
6310 case '!':
6311 num = backgndpid;
6312 if (num == 0)
6313 return -1;
6314 numvar:
6315 len = cvtnum(num);
6316 break;
6317 case '-':
6318 p = makestrspace(NOPTS, expdest);
6319 for (i = NOPTS - 1; i >= 0; i--) {
6320 if (optlist[i]) {
6321 USTPUTC(optletters(i), p);
6322 len++;
6323 }
6324 }
6325 expdest = p;
6326 break;
6327 case '@':
6328 if (sep)
6329 goto param;
6330 /* fall through */
6331 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006332 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006333 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6334 sepq = 1;
6335 param:
6336 ap = shellparam.p;
6337 if (!ap)
6338 return -1;
6339 while ((p = *ap++)) {
6340 size_t partlen;
6341
6342 partlen = strlen(p);
6343 len += partlen;
6344
6345 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6346 memtodest(p, partlen, syntax, quotes);
6347
6348 if (*ap && sep) {
6349 char *q;
6350
6351 len++;
6352 if (subtype == VSPLUS || subtype == VSLENGTH) {
6353 continue;
6354 }
6355 q = expdest;
6356 if (sepq)
6357 STPUTC(CTLESC, q);
6358 STPUTC(sep, q);
6359 expdest = q;
6360 }
6361 }
6362 return len;
6363 case '0':
6364 case '1':
6365 case '2':
6366 case '3':
6367 case '4':
6368 case '5':
6369 case '6':
6370 case '7':
6371 case '8':
6372 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006373// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006374 num = atoi(name);
6375 if (num < 0 || num > shellparam.nparam)
6376 return -1;
6377 p = num ? shellparam.p[num - 1] : arg0;
6378 goto value;
6379 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006380 /* NB: name has form "VAR=..." */
6381
6382 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6383 * which should be considered before we check variables. */
6384 if (var_str_list) {
6385 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6386 p = NULL;
6387 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006388 char *str, *eq;
6389 str = var_str_list->text;
6390 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006391 if (!eq) /* stop at first non-assignment */
6392 break;
6393 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006394 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006395 && strncmp(str, name, name_len) == 0) {
6396 p = eq;
6397 /* goto value; - WRONG! */
6398 /* think "A=1 A=2 B=$A" */
6399 }
6400 var_str_list = var_str_list->next;
6401 } while (var_str_list);
6402 if (p)
6403 goto value;
6404 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405 p = lookupvar(name);
6406 value:
6407 if (!p)
6408 return -1;
6409
6410 len = strlen(p);
6411 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6412 memtodest(p, len, syntax, quotes);
6413 return len;
6414 }
6415
6416 if (subtype == VSPLUS || subtype == VSLENGTH)
6417 STADJUST(-len, expdest);
6418 return len;
6419}
6420
6421/*
6422 * Expand a variable, and return a pointer to the next character in the
6423 * input string.
6424 */
6425static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006426evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006428 char varflags;
6429 char subtype;
6430 char quoted;
6431 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006432 char *var;
6433 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006434 int startloc;
6435 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006437 varflags = *p++;
6438 subtype = varflags & VSTYPE;
6439 quoted = varflags & VSQUOTE;
6440 var = p;
6441 easy = (!quoted || (*var == '@' && shellparam.nparam));
6442 startloc = expdest - (char *)stackblock();
6443 p = strchr(p, '=') + 1;
6444
6445 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006446 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006447 if (varflags & VSNUL)
6448 varlen--;
6449
6450 if (subtype == VSPLUS) {
6451 varlen = -1 - varlen;
6452 goto vsplus;
6453 }
6454
6455 if (subtype == VSMINUS) {
6456 vsplus:
6457 if (varlen < 0) {
6458 argstr(
6459 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006460 (quoted ? EXP_QWORD : EXP_WORD),
6461 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006462 );
6463 goto end;
6464 }
6465 if (easy)
6466 goto record;
6467 goto end;
6468 }
6469
6470 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6471 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006472 if (subevalvar(p, var, /* strloc: */ 0,
6473 subtype, startloc, varflags,
6474 /* quotes: */ 0,
6475 var_str_list)
6476 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006477 varflags &= ~VSNUL;
6478 /*
6479 * Remove any recorded regions beyond
6480 * start of variable
6481 */
6482 removerecordregions(startloc);
6483 goto again;
6484 }
6485 goto end;
6486 }
6487 if (easy)
6488 goto record;
6489 goto end;
6490 }
6491
6492 if (varlen < 0 && uflag)
6493 varunset(p, var, 0, 0);
6494
6495 if (subtype == VSLENGTH) {
6496 cvtnum(varlen > 0 ? varlen : 0);
6497 goto record;
6498 }
6499
6500 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006501 if (easy)
6502 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006503 goto end;
6504 }
6505
6506#if DEBUG
6507 switch (subtype) {
6508 case VSTRIMLEFT:
6509 case VSTRIMLEFTMAX:
6510 case VSTRIMRIGHT:
6511 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006512#if ENABLE_ASH_BASH_COMPAT
6513 case VSSUBSTR:
6514 case VSREPLACE:
6515 case VSREPLACEALL:
6516#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006517 break;
6518 default:
6519 abort();
6520 }
6521#endif
6522
6523 if (varlen >= 0) {
6524 /*
6525 * Terminate the string and start recording the pattern
6526 * right after it
6527 */
6528 STPUTC('\0', expdest);
6529 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006530 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6531 startloc, varflags,
6532 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6533 var_str_list)
6534 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006535 int amount = expdest - (
6536 (char *)stackblock() + patloc - 1
6537 );
6538 STADJUST(-amount, expdest);
6539 }
6540 /* Remove any recorded regions beyond start of variable */
6541 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006542 record:
6543 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 }
6545
6546 end:
6547 if (subtype != VSNORMAL) { /* skip to end of alternative */
6548 int nesting = 1;
6549 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006550 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006551 if (c == CTLESC)
6552 p++;
6553 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6554 if (varlen >= 0)
6555 argbackq = argbackq->next;
6556 } else if (c == CTLVAR) {
6557 if ((*p++ & VSTYPE) != VSNORMAL)
6558 nesting++;
6559 } else if (c == CTLENDVAR) {
6560 if (--nesting == 0)
6561 break;
6562 }
6563 }
6564 }
6565 return p;
6566}
6567
6568/*
6569 * Break the argument string into pieces based upon IFS and add the
6570 * strings to the argument list. The regions of the string to be
6571 * searched for IFS characters have been stored by recordregion.
6572 */
6573static void
6574ifsbreakup(char *string, struct arglist *arglist)
6575{
6576 struct ifsregion *ifsp;
6577 struct strlist *sp;
6578 char *start;
6579 char *p;
6580 char *q;
6581 const char *ifs, *realifs;
6582 int ifsspc;
6583 int nulonly;
6584
6585 start = string;
6586 if (ifslastp != NULL) {
6587 ifsspc = 0;
6588 nulonly = 0;
6589 realifs = ifsset() ? ifsval() : defifs;
6590 ifsp = &ifsfirst;
6591 do {
6592 p = string + ifsp->begoff;
6593 nulonly = ifsp->nulonly;
6594 ifs = nulonly ? nullstr : realifs;
6595 ifsspc = 0;
6596 while (p < string + ifsp->endoff) {
6597 q = p;
6598 if (*p == CTLESC)
6599 p++;
6600 if (!strchr(ifs, *p)) {
6601 p++;
6602 continue;
6603 }
6604 if (!nulonly)
6605 ifsspc = (strchr(defifs, *p) != NULL);
6606 /* Ignore IFS whitespace at start */
6607 if (q == start && ifsspc) {
6608 p++;
6609 start = p;
6610 continue;
6611 }
6612 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006613 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006614 sp->text = start;
6615 *arglist->lastp = sp;
6616 arglist->lastp = &sp->next;
6617 p++;
6618 if (!nulonly) {
6619 for (;;) {
6620 if (p >= string + ifsp->endoff) {
6621 break;
6622 }
6623 q = p;
6624 if (*p == CTLESC)
6625 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006626 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006627 p = q;
6628 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006629 }
6630 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006631 if (ifsspc) {
6632 p++;
6633 ifsspc = 0;
6634 } else {
6635 p = q;
6636 break;
6637 }
6638 } else
6639 p++;
6640 }
6641 }
6642 start = p;
6643 } /* while */
6644 ifsp = ifsp->next;
6645 } while (ifsp != NULL);
6646 if (nulonly)
6647 goto add;
6648 }
6649
6650 if (!*start)
6651 return;
6652
6653 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006654 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655 sp->text = start;
6656 *arglist->lastp = sp;
6657 arglist->lastp = &sp->next;
6658}
6659
6660static void
6661ifsfree(void)
6662{
6663 struct ifsregion *p;
6664
6665 INT_OFF;
6666 p = ifsfirst.next;
6667 do {
6668 struct ifsregion *ifsp;
6669 ifsp = p->next;
6670 free(p);
6671 p = ifsp;
6672 } while (p);
6673 ifslastp = NULL;
6674 ifsfirst.next = NULL;
6675 INT_ON;
6676}
6677
6678/*
6679 * Add a file name to the list.
6680 */
6681static void
6682addfname(const char *name)
6683{
6684 struct strlist *sp;
6685
Denis Vlasenko597906c2008-02-20 16:38:54 +00006686 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006687 sp->text = ststrdup(name);
6688 *exparg.lastp = sp;
6689 exparg.lastp = &sp->next;
6690}
6691
6692static char *expdir;
6693
6694/*
6695 * Do metacharacter (i.e. *, ?, [...]) expansion.
6696 */
6697static void
6698expmeta(char *enddir, char *name)
6699{
6700 char *p;
6701 const char *cp;
6702 char *start;
6703 char *endname;
6704 int metaflag;
6705 struct stat statb;
6706 DIR *dirp;
6707 struct dirent *dp;
6708 int atend;
6709 int matchdot;
6710
6711 metaflag = 0;
6712 start = name;
6713 for (p = name; *p; p++) {
6714 if (*p == '*' || *p == '?')
6715 metaflag = 1;
6716 else if (*p == '[') {
6717 char *q = p + 1;
6718 if (*q == '!')
6719 q++;
6720 for (;;) {
6721 if (*q == '\\')
6722 q++;
6723 if (*q == '/' || *q == '\0')
6724 break;
6725 if (*++q == ']') {
6726 metaflag = 1;
6727 break;
6728 }
6729 }
6730 } else if (*p == '\\')
6731 p++;
6732 else if (*p == '/') {
6733 if (metaflag)
6734 goto out;
6735 start = p + 1;
6736 }
6737 }
6738 out:
6739 if (metaflag == 0) { /* we've reached the end of the file name */
6740 if (enddir != expdir)
6741 metaflag++;
6742 p = name;
6743 do {
6744 if (*p == '\\')
6745 p++;
6746 *enddir++ = *p;
6747 } while (*p++);
6748 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6749 addfname(expdir);
6750 return;
6751 }
6752 endname = p;
6753 if (name < start) {
6754 p = name;
6755 do {
6756 if (*p == '\\')
6757 p++;
6758 *enddir++ = *p++;
6759 } while (p < start);
6760 }
6761 if (enddir == expdir) {
6762 cp = ".";
6763 } else if (enddir == expdir + 1 && *expdir == '/') {
6764 cp = "/";
6765 } else {
6766 cp = expdir;
6767 enddir[-1] = '\0';
6768 }
6769 dirp = opendir(cp);
6770 if (dirp == NULL)
6771 return;
6772 if (enddir != expdir)
6773 enddir[-1] = '/';
6774 if (*endname == 0) {
6775 atend = 1;
6776 } else {
6777 atend = 0;
6778 *endname++ = '\0';
6779 }
6780 matchdot = 0;
6781 p = start;
6782 if (*p == '\\')
6783 p++;
6784 if (*p == '.')
6785 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006786 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006787 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006788 continue;
6789 if (pmatch(start, dp->d_name)) {
6790 if (atend) {
6791 strcpy(enddir, dp->d_name);
6792 addfname(expdir);
6793 } else {
6794 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6795 continue;
6796 p[-1] = '/';
6797 expmeta(p, endname);
6798 }
6799 }
6800 }
6801 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006802 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006803 endname[-1] = '/';
6804}
6805
6806static struct strlist *
6807msort(struct strlist *list, int len)
6808{
6809 struct strlist *p, *q = NULL;
6810 struct strlist **lpp;
6811 int half;
6812 int n;
6813
6814 if (len <= 1)
6815 return list;
6816 half = len >> 1;
6817 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006818 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006819 q = p;
6820 p = p->next;
6821 }
6822 q->next = NULL; /* terminate first half of list */
6823 q = msort(list, half); /* sort first half of list */
6824 p = msort(p, len - half); /* sort second half */
6825 lpp = &list;
6826 for (;;) {
6827#if ENABLE_LOCALE_SUPPORT
6828 if (strcoll(p->text, q->text) < 0)
6829#else
6830 if (strcmp(p->text, q->text) < 0)
6831#endif
6832 {
6833 *lpp = p;
6834 lpp = &p->next;
6835 p = *lpp;
6836 if (p == NULL) {
6837 *lpp = q;
6838 break;
6839 }
6840 } else {
6841 *lpp = q;
6842 lpp = &q->next;
6843 q = *lpp;
6844 if (q == NULL) {
6845 *lpp = p;
6846 break;
6847 }
6848 }
6849 }
6850 return list;
6851}
6852
6853/*
6854 * Sort the results of file name expansion. It calculates the number of
6855 * strings to sort and then calls msort (short for merge sort) to do the
6856 * work.
6857 */
6858static struct strlist *
6859expsort(struct strlist *str)
6860{
6861 int len;
6862 struct strlist *sp;
6863
6864 len = 0;
6865 for (sp = str; sp; sp = sp->next)
6866 len++;
6867 return msort(str, len);
6868}
6869
6870static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006871expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006872{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006873 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006874 '*', '?', '[', 0
6875 };
6876 /* TODO - EXP_REDIR */
6877
6878 while (str) {
6879 struct strlist **savelastp;
6880 struct strlist *sp;
6881 char *p;
6882
6883 if (fflag)
6884 goto nometa;
6885 if (!strpbrk(str->text, metachars))
6886 goto nometa;
6887 savelastp = exparg.lastp;
6888
6889 INT_OFF;
6890 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6891 {
6892 int i = strlen(str->text);
6893 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6894 }
6895
6896 expmeta(expdir, p);
6897 free(expdir);
6898 if (p != str->text)
6899 free(p);
6900 INT_ON;
6901 if (exparg.lastp == savelastp) {
6902 /*
6903 * no matches
6904 */
6905 nometa:
6906 *exparg.lastp = str;
6907 rmescapes(str->text);
6908 exparg.lastp = &str->next;
6909 } else {
6910 *exparg.lastp = NULL;
6911 *savelastp = sp = expsort(*savelastp);
6912 while (sp->next != NULL)
6913 sp = sp->next;
6914 exparg.lastp = &sp->next;
6915 }
6916 str = str->next;
6917 }
6918}
6919
6920/*
6921 * Perform variable substitution and command substitution on an argument,
6922 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6923 * perform splitting and file name expansion. When arglist is NULL, perform
6924 * here document expansion.
6925 */
6926static void
6927expandarg(union node *arg, struct arglist *arglist, int flag)
6928{
6929 struct strlist *sp;
6930 char *p;
6931
6932 argbackq = arg->narg.backquote;
6933 STARTSTACKSTR(expdest);
6934 ifsfirst.next = NULL;
6935 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006936 argstr(arg->narg.text, flag,
6937 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938 p = _STPUTC('\0', expdest);
6939 expdest = p - 1;
6940 if (arglist == NULL) {
6941 return; /* here document expanded */
6942 }
6943 p = grabstackstr(p);
6944 exparg.lastp = &exparg.list;
6945 /*
6946 * TODO - EXP_REDIR
6947 */
6948 if (flag & EXP_FULL) {
6949 ifsbreakup(p, &exparg);
6950 *exparg.lastp = NULL;
6951 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006952 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006953 } else {
6954 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6955 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006956 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006957 sp->text = p;
6958 *exparg.lastp = sp;
6959 exparg.lastp = &sp->next;
6960 }
6961 if (ifsfirst.next)
6962 ifsfree();
6963 *exparg.lastp = NULL;
6964 if (exparg.list) {
6965 *arglist->lastp = exparg.list;
6966 arglist->lastp = exparg.lastp;
6967 }
6968}
6969
6970/*
6971 * Expand shell variables and backquotes inside a here document.
6972 */
6973static void
6974expandhere(union node *arg, int fd)
6975{
6976 herefd = fd;
6977 expandarg(arg, (struct arglist *)NULL, 0);
6978 full_write(fd, stackblock(), expdest - (char *)stackblock());
6979}
6980
6981/*
6982 * Returns true if the pattern matches the string.
6983 */
6984static int
6985patmatch(char *pattern, const char *string)
6986{
6987 return pmatch(preglob(pattern, 0, 0), string);
6988}
6989
6990/*
6991 * See if a pattern matches in a case statement.
6992 */
6993static int
6994casematch(union node *pattern, char *val)
6995{
6996 struct stackmark smark;
6997 int result;
6998
6999 setstackmark(&smark);
7000 argbackq = pattern->narg.backquote;
7001 STARTSTACKSTR(expdest);
7002 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007003 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7004 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007005 STACKSTRNUL(expdest);
7006 result = patmatch(stackblock(), val);
7007 popstackmark(&smark);
7008 return result;
7009}
7010
7011
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007012/* ============ find_command */
7013
7014struct builtincmd {
7015 const char *name;
7016 int (*builtin)(int, char **);
7017 /* unsigned flags; */
7018};
7019#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007020/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007021 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007022#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007023#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007024
7025struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007026 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007027 union param {
7028 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007029 /* index >= 0 for commands without path (slashes) */
7030 /* (TODO: what exactly does the value mean? PATH position?) */
7031 /* index == -1 for commands with slashes */
7032 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007033 const struct builtincmd *cmd;
7034 struct funcnode *func;
7035 } u;
7036};
7037/* values of cmdtype */
7038#define CMDUNKNOWN -1 /* no entry in table for command */
7039#define CMDNORMAL 0 /* command is an executable program */
7040#define CMDFUNCTION 1 /* command is a shell function */
7041#define CMDBUILTIN 2 /* command is a shell builtin */
7042
7043/* action to find_command() */
7044#define DO_ERR 0x01 /* prints errors */
7045#define DO_ABS 0x02 /* checks absolute paths */
7046#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7047#define DO_ALTPATH 0x08 /* using alternate path */
7048#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7049
7050static void find_command(char *, struct cmdentry *, int, const char *);
7051
7052
7053/* ============ Hashing commands */
7054
7055/*
7056 * When commands are first encountered, they are entered in a hash table.
7057 * This ensures that a full path search will not have to be done for them
7058 * on each invocation.
7059 *
7060 * We should investigate converting to a linear search, even though that
7061 * would make the command name "hash" a misnomer.
7062 */
7063
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007064struct tblentry {
7065 struct tblentry *next; /* next entry in hash chain */
7066 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007067 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007068 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007069 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007070};
7071
Denis Vlasenko01631112007-12-16 17:20:38 +00007072static struct tblentry **cmdtable;
7073#define INIT_G_cmdtable() do { \
7074 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7075} while (0)
7076
7077static int builtinloc = -1; /* index in path of %builtin, or -1 */
7078
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007079
7080static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007081tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007082{
7083 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007084
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007085#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007086 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007087 if (APPLET_IS_NOEXEC(applet_no)) {
7088 while (*envp)
7089 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007090 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007091 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007092 /* re-exec ourselves with the new arguments */
7093 execve(bb_busybox_exec_path, argv, envp);
7094 /* If they called chroot or otherwise made the binary no longer
7095 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007096 }
7097#endif
7098
7099 repeat:
7100#ifdef SYSV
7101 do {
7102 execve(cmd, argv, envp);
7103 } while (errno == EINTR);
7104#else
7105 execve(cmd, argv, envp);
7106#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007107 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007108 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007109 return;
7110 }
7111 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007112 char **ap;
7113 char **new;
7114
7115 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007116 continue;
7117 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007118 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007119 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007120 ap += 2;
7121 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007122 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007123 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007124 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007125 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007126 goto repeat;
7127 }
7128}
7129
7130/*
7131 * Exec a program. Never returns. If you change this routine, you may
7132 * have to change the find_command routine as well.
7133 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007134static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007135static void
7136shellexec(char **argv, const char *path, int idx)
7137{
7138 char *cmdname;
7139 int e;
7140 char **envp;
7141 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007142#if ENABLE_FEATURE_SH_STANDALONE
7143 int applet_no = -1;
7144#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007145
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007146 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007147 envp = listvars(VEXPORT, VUNSET, 0);
7148 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007149#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007150 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007151#endif
7152 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007153 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007154 e = errno;
7155 } else {
7156 e = ENOENT;
7157 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7158 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007159 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007160 if (errno != ENOENT && errno != ENOTDIR)
7161 e = errno;
7162 }
7163 stunalloc(cmdname);
7164 }
7165 }
7166
7167 /* Map to POSIX errors */
7168 switch (e) {
7169 case EACCES:
7170 exerrno = 126;
7171 break;
7172 case ENOENT:
7173 exerrno = 127;
7174 break;
7175 default:
7176 exerrno = 2;
7177 break;
7178 }
7179 exitstatus = exerrno;
7180 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007181 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007182 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7183 /* NOTREACHED */
7184}
7185
7186static void
7187printentry(struct tblentry *cmdp)
7188{
7189 int idx;
7190 const char *path;
7191 char *name;
7192
7193 idx = cmdp->param.index;
7194 path = pathval();
7195 do {
7196 name = padvance(&path, cmdp->cmdname);
7197 stunalloc(name);
7198 } while (--idx >= 0);
7199 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7200}
7201
7202/*
7203 * Clear out command entries. The argument specifies the first entry in
7204 * PATH which has changed.
7205 */
7206static void
7207clearcmdentry(int firstchange)
7208{
7209 struct tblentry **tblp;
7210 struct tblentry **pp;
7211 struct tblentry *cmdp;
7212
7213 INT_OFF;
7214 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7215 pp = tblp;
7216 while ((cmdp = *pp) != NULL) {
7217 if ((cmdp->cmdtype == CMDNORMAL &&
7218 cmdp->param.index >= firstchange)
7219 || (cmdp->cmdtype == CMDBUILTIN &&
7220 builtinloc >= firstchange)
7221 ) {
7222 *pp = cmdp->next;
7223 free(cmdp);
7224 } else {
7225 pp = &cmdp->next;
7226 }
7227 }
7228 }
7229 INT_ON;
7230}
7231
7232/*
7233 * Locate a command in the command hash table. If "add" is nonzero,
7234 * add the command to the table if it is not already present. The
7235 * variable "lastcmdentry" is set to point to the address of the link
7236 * pointing to the entry, so that delete_cmd_entry can delete the
7237 * entry.
7238 *
7239 * Interrupts must be off if called with add != 0.
7240 */
7241static struct tblentry **lastcmdentry;
7242
7243static struct tblentry *
7244cmdlookup(const char *name, int add)
7245{
7246 unsigned int hashval;
7247 const char *p;
7248 struct tblentry *cmdp;
7249 struct tblentry **pp;
7250
7251 p = name;
7252 hashval = (unsigned char)*p << 4;
7253 while (*p)
7254 hashval += (unsigned char)*p++;
7255 hashval &= 0x7FFF;
7256 pp = &cmdtable[hashval % CMDTABLESIZE];
7257 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7258 if (strcmp(cmdp->cmdname, name) == 0)
7259 break;
7260 pp = &cmdp->next;
7261 }
7262 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007263 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7264 + strlen(name)
7265 /* + 1 - already done because
7266 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007267 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007268 cmdp->cmdtype = CMDUNKNOWN;
7269 strcpy(cmdp->cmdname, name);
7270 }
7271 lastcmdentry = pp;
7272 return cmdp;
7273}
7274
7275/*
7276 * Delete the command entry returned on the last lookup.
7277 */
7278static void
7279delete_cmd_entry(void)
7280{
7281 struct tblentry *cmdp;
7282
7283 INT_OFF;
7284 cmdp = *lastcmdentry;
7285 *lastcmdentry = cmdp->next;
7286 if (cmdp->cmdtype == CMDFUNCTION)
7287 freefunc(cmdp->param.func);
7288 free(cmdp);
7289 INT_ON;
7290}
7291
7292/*
7293 * Add a new command entry, replacing any existing command entry for
7294 * the same name - except special builtins.
7295 */
7296static void
7297addcmdentry(char *name, struct cmdentry *entry)
7298{
7299 struct tblentry *cmdp;
7300
7301 cmdp = cmdlookup(name, 1);
7302 if (cmdp->cmdtype == CMDFUNCTION) {
7303 freefunc(cmdp->param.func);
7304 }
7305 cmdp->cmdtype = entry->cmdtype;
7306 cmdp->param = entry->u;
7307 cmdp->rehash = 0;
7308}
7309
7310static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007311hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007312{
7313 struct tblentry **pp;
7314 struct tblentry *cmdp;
7315 int c;
7316 struct cmdentry entry;
7317 char *name;
7318
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007319 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007320 clearcmdentry(0);
7321 return 0;
7322 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007323
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007324 if (*argptr == NULL) {
7325 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7326 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7327 if (cmdp->cmdtype == CMDNORMAL)
7328 printentry(cmdp);
7329 }
7330 }
7331 return 0;
7332 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007333
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007334 c = 0;
7335 while ((name = *argptr) != NULL) {
7336 cmdp = cmdlookup(name, 0);
7337 if (cmdp != NULL
7338 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007339 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7340 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007341 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007342 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007343 find_command(name, &entry, DO_ERR, pathval());
7344 if (entry.cmdtype == CMDUNKNOWN)
7345 c = 1;
7346 argptr++;
7347 }
7348 return c;
7349}
7350
7351/*
7352 * Called when a cd is done. Marks all commands so the next time they
7353 * are executed they will be rehashed.
7354 */
7355static void
7356hashcd(void)
7357{
7358 struct tblentry **pp;
7359 struct tblentry *cmdp;
7360
7361 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7362 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007363 if (cmdp->cmdtype == CMDNORMAL
7364 || (cmdp->cmdtype == CMDBUILTIN
7365 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7366 && builtinloc > 0)
7367 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007368 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007369 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007370 }
7371 }
7372}
7373
7374/*
7375 * Fix command hash table when PATH changed.
7376 * Called before PATH is changed. The argument is the new value of PATH;
7377 * pathval() still returns the old value at this point.
7378 * Called with interrupts off.
7379 */
7380static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007381changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007382{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007383 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007384 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007385 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007386 int idx_bltin;
7387
7388 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389 firstchange = 9999; /* assume no change */
7390 idx = 0;
7391 idx_bltin = -1;
7392 for (;;) {
7393 if (*old != *new) {
7394 firstchange = idx;
7395 if ((*old == '\0' && *new == ':')
7396 || (*old == ':' && *new == '\0'))
7397 firstchange++;
7398 old = new; /* ignore subsequent differences */
7399 }
7400 if (*new == '\0')
7401 break;
7402 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7403 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007404 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007405 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406 new++, old++;
7407 }
7408 if (builtinloc < 0 && idx_bltin >= 0)
7409 builtinloc = idx_bltin; /* zap builtins */
7410 if (builtinloc >= 0 && idx_bltin < 0)
7411 firstchange = 0;
7412 clearcmdentry(firstchange);
7413 builtinloc = idx_bltin;
7414}
7415
7416#define TEOF 0
7417#define TNL 1
7418#define TREDIR 2
7419#define TWORD 3
7420#define TSEMI 4
7421#define TBACKGND 5
7422#define TAND 6
7423#define TOR 7
7424#define TPIPE 8
7425#define TLP 9
7426#define TRP 10
7427#define TENDCASE 11
7428#define TENDBQUOTE 12
7429#define TNOT 13
7430#define TCASE 14
7431#define TDO 15
7432#define TDONE 16
7433#define TELIF 17
7434#define TELSE 18
7435#define TESAC 19
7436#define TFI 20
7437#define TFOR 21
7438#define TIF 22
7439#define TIN 23
7440#define TTHEN 24
7441#define TUNTIL 25
7442#define TWHILE 26
7443#define TBEGIN 27
7444#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007445typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007446
7447/* first char is indicating which tokens mark the end of a list */
7448static const char *const tokname_array[] = {
7449 "\1end of file",
7450 "\0newline",
7451 "\0redirection",
7452 "\0word",
7453 "\0;",
7454 "\0&",
7455 "\0&&",
7456 "\0||",
7457 "\0|",
7458 "\0(",
7459 "\1)",
7460 "\1;;",
7461 "\1`",
7462#define KWDOFFSET 13
7463 /* the following are keywords */
7464 "\0!",
7465 "\0case",
7466 "\1do",
7467 "\1done",
7468 "\1elif",
7469 "\1else",
7470 "\1esac",
7471 "\1fi",
7472 "\0for",
7473 "\0if",
7474 "\0in",
7475 "\1then",
7476 "\0until",
7477 "\0while",
7478 "\0{",
7479 "\1}",
7480};
7481
7482static const char *
7483tokname(int tok)
7484{
7485 static char buf[16];
7486
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007487//try this:
7488//if (tok < TSEMI) return tokname_array[tok] + 1;
7489//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7490//return buf;
7491
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007492 if (tok >= TSEMI)
7493 buf[0] = '"';
7494 sprintf(buf + (tok >= TSEMI), "%s%c",
7495 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7496 return buf;
7497}
7498
7499/* Wrapper around strcmp for qsort/bsearch/... */
7500static int
7501pstrcmp(const void *a, const void *b)
7502{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007503 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007504}
7505
7506static const char *const *
7507findkwd(const char *s)
7508{
7509 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007510 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7511 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007512}
7513
7514/*
7515 * Locate and print what a word is...
7516 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007517static int
7518describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007519{
7520 struct cmdentry entry;
7521 struct tblentry *cmdp;
7522#if ENABLE_ASH_ALIAS
7523 const struct alias *ap;
7524#endif
7525 const char *path = pathval();
7526
7527 if (describe_command_verbose) {
7528 out1str(command);
7529 }
7530
7531 /* First look at the keywords */
7532 if (findkwd(command)) {
7533 out1str(describe_command_verbose ? " is a shell keyword" : command);
7534 goto out;
7535 }
7536
7537#if ENABLE_ASH_ALIAS
7538 /* Then look at the aliases */
7539 ap = lookupalias(command, 0);
7540 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007541 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007542 out1str("alias ");
7543 printalias(ap);
7544 return 0;
7545 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007546 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007547 goto out;
7548 }
7549#endif
7550 /* Then check if it is a tracked alias */
7551 cmdp = cmdlookup(command, 0);
7552 if (cmdp != NULL) {
7553 entry.cmdtype = cmdp->cmdtype;
7554 entry.u = cmdp->param;
7555 } else {
7556 /* Finally use brute force */
7557 find_command(command, &entry, DO_ABS, path);
7558 }
7559
7560 switch (entry.cmdtype) {
7561 case CMDNORMAL: {
7562 int j = entry.u.index;
7563 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007564 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007565 p = command;
7566 } else {
7567 do {
7568 p = padvance(&path, command);
7569 stunalloc(p);
7570 } while (--j >= 0);
7571 }
7572 if (describe_command_verbose) {
7573 out1fmt(" is%s %s",
7574 (cmdp ? " a tracked alias for" : nullstr), p
7575 );
7576 } else {
7577 out1str(p);
7578 }
7579 break;
7580 }
7581
7582 case CMDFUNCTION:
7583 if (describe_command_verbose) {
7584 out1str(" is a shell function");
7585 } else {
7586 out1str(command);
7587 }
7588 break;
7589
7590 case CMDBUILTIN:
7591 if (describe_command_verbose) {
7592 out1fmt(" is a %sshell builtin",
7593 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7594 "special " : nullstr
7595 );
7596 } else {
7597 out1str(command);
7598 }
7599 break;
7600
7601 default:
7602 if (describe_command_verbose) {
7603 out1str(": not found\n");
7604 }
7605 return 127;
7606 }
7607 out:
7608 outstr("\n", stdout);
7609 return 0;
7610}
7611
7612static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007613typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007614{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007615 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007616 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007617 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007618
Denis Vlasenko46846e22007-05-20 13:08:31 +00007619 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007620 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007621 i++;
7622 verbose = 0;
7623 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007624 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007625 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007626 }
7627 return err;
7628}
7629
7630#if ENABLE_ASH_CMDCMD
7631static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007632commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007633{
7634 int c;
7635 enum {
7636 VERIFY_BRIEF = 1,
7637 VERIFY_VERBOSE = 2,
7638 } verify = 0;
7639
7640 while ((c = nextopt("pvV")) != '\0')
7641 if (c == 'V')
7642 verify |= VERIFY_VERBOSE;
7643 else if (c == 'v')
7644 verify |= VERIFY_BRIEF;
7645#if DEBUG
7646 else if (c != 'p')
7647 abort();
7648#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007649 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7650 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007651 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007652 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653
7654 return 0;
7655}
7656#endif
7657
7658
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007659/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007660
Denis Vlasenko340299a2008-11-21 10:36:36 +00007661static int funcblocksize; /* size of structures in function */
7662static int funcstringsize; /* size of strings in node */
7663static void *funcblock; /* block to allocate function from */
7664static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007665
Eric Andersencb57d552001-06-28 07:25:16 +00007666/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007667#define EV_EXIT 01 /* exit after evaluating tree */
7668#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007669#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007670
Denis Vlasenko340299a2008-11-21 10:36:36 +00007671static const short nodesize[N_NUMBER] = {
7672 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7673 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7674 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7675 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7676 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7677 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7678 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7679 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7680 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7681 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7682 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7683 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7684 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7685 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7686 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7687 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7688 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007689#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007690 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007691#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007692 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7693 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7694 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7695 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7696 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7697 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7698 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7699 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7700 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007701};
7702
7703static void calcsize(union node *n);
7704
7705static void
7706sizenodelist(struct nodelist *lp)
7707{
7708 while (lp) {
7709 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7710 calcsize(lp->n);
7711 lp = lp->next;
7712 }
7713}
7714
7715static void
7716calcsize(union node *n)
7717{
7718 if (n == NULL)
7719 return;
7720 funcblocksize += nodesize[n->type];
7721 switch (n->type) {
7722 case NCMD:
7723 calcsize(n->ncmd.redirect);
7724 calcsize(n->ncmd.args);
7725 calcsize(n->ncmd.assign);
7726 break;
7727 case NPIPE:
7728 sizenodelist(n->npipe.cmdlist);
7729 break;
7730 case NREDIR:
7731 case NBACKGND:
7732 case NSUBSHELL:
7733 calcsize(n->nredir.redirect);
7734 calcsize(n->nredir.n);
7735 break;
7736 case NAND:
7737 case NOR:
7738 case NSEMI:
7739 case NWHILE:
7740 case NUNTIL:
7741 calcsize(n->nbinary.ch2);
7742 calcsize(n->nbinary.ch1);
7743 break;
7744 case NIF:
7745 calcsize(n->nif.elsepart);
7746 calcsize(n->nif.ifpart);
7747 calcsize(n->nif.test);
7748 break;
7749 case NFOR:
7750 funcstringsize += strlen(n->nfor.var) + 1;
7751 calcsize(n->nfor.body);
7752 calcsize(n->nfor.args);
7753 break;
7754 case NCASE:
7755 calcsize(n->ncase.cases);
7756 calcsize(n->ncase.expr);
7757 break;
7758 case NCLIST:
7759 calcsize(n->nclist.body);
7760 calcsize(n->nclist.pattern);
7761 calcsize(n->nclist.next);
7762 break;
7763 case NDEFUN:
7764 case NARG:
7765 sizenodelist(n->narg.backquote);
7766 funcstringsize += strlen(n->narg.text) + 1;
7767 calcsize(n->narg.next);
7768 break;
7769 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007770#if ENABLE_ASH_BASH_COMPAT
7771 case NTO2:
7772#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007773 case NCLOBBER:
7774 case NFROM:
7775 case NFROMTO:
7776 case NAPPEND:
7777 calcsize(n->nfile.fname);
7778 calcsize(n->nfile.next);
7779 break;
7780 case NTOFD:
7781 case NFROMFD:
7782 calcsize(n->ndup.vname);
7783 calcsize(n->ndup.next);
7784 break;
7785 case NHERE:
7786 case NXHERE:
7787 calcsize(n->nhere.doc);
7788 calcsize(n->nhere.next);
7789 break;
7790 case NNOT:
7791 calcsize(n->nnot.com);
7792 break;
7793 };
7794}
7795
7796static char *
7797nodeckstrdup(char *s)
7798{
7799 char *rtn = funcstring;
7800
7801 strcpy(funcstring, s);
7802 funcstring += strlen(s) + 1;
7803 return rtn;
7804}
7805
7806static union node *copynode(union node *);
7807
7808static struct nodelist *
7809copynodelist(struct nodelist *lp)
7810{
7811 struct nodelist *start;
7812 struct nodelist **lpp;
7813
7814 lpp = &start;
7815 while (lp) {
7816 *lpp = funcblock;
7817 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7818 (*lpp)->n = copynode(lp->n);
7819 lp = lp->next;
7820 lpp = &(*lpp)->next;
7821 }
7822 *lpp = NULL;
7823 return start;
7824}
7825
7826static union node *
7827copynode(union node *n)
7828{
7829 union node *new;
7830
7831 if (n == NULL)
7832 return NULL;
7833 new = funcblock;
7834 funcblock = (char *) funcblock + nodesize[n->type];
7835
7836 switch (n->type) {
7837 case NCMD:
7838 new->ncmd.redirect = copynode(n->ncmd.redirect);
7839 new->ncmd.args = copynode(n->ncmd.args);
7840 new->ncmd.assign = copynode(n->ncmd.assign);
7841 break;
7842 case NPIPE:
7843 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007844 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007845 break;
7846 case NREDIR:
7847 case NBACKGND:
7848 case NSUBSHELL:
7849 new->nredir.redirect = copynode(n->nredir.redirect);
7850 new->nredir.n = copynode(n->nredir.n);
7851 break;
7852 case NAND:
7853 case NOR:
7854 case NSEMI:
7855 case NWHILE:
7856 case NUNTIL:
7857 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7858 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7859 break;
7860 case NIF:
7861 new->nif.elsepart = copynode(n->nif.elsepart);
7862 new->nif.ifpart = copynode(n->nif.ifpart);
7863 new->nif.test = copynode(n->nif.test);
7864 break;
7865 case NFOR:
7866 new->nfor.var = nodeckstrdup(n->nfor.var);
7867 new->nfor.body = copynode(n->nfor.body);
7868 new->nfor.args = copynode(n->nfor.args);
7869 break;
7870 case NCASE:
7871 new->ncase.cases = copynode(n->ncase.cases);
7872 new->ncase.expr = copynode(n->ncase.expr);
7873 break;
7874 case NCLIST:
7875 new->nclist.body = copynode(n->nclist.body);
7876 new->nclist.pattern = copynode(n->nclist.pattern);
7877 new->nclist.next = copynode(n->nclist.next);
7878 break;
7879 case NDEFUN:
7880 case NARG:
7881 new->narg.backquote = copynodelist(n->narg.backquote);
7882 new->narg.text = nodeckstrdup(n->narg.text);
7883 new->narg.next = copynode(n->narg.next);
7884 break;
7885 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007886#if ENABLE_ASH_BASH_COMPAT
7887 case NTO2:
7888#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007889 case NCLOBBER:
7890 case NFROM:
7891 case NFROMTO:
7892 case NAPPEND:
7893 new->nfile.fname = copynode(n->nfile.fname);
7894 new->nfile.fd = n->nfile.fd;
7895 new->nfile.next = copynode(n->nfile.next);
7896 break;
7897 case NTOFD:
7898 case NFROMFD:
7899 new->ndup.vname = copynode(n->ndup.vname);
7900 new->ndup.dupfd = n->ndup.dupfd;
7901 new->ndup.fd = n->ndup.fd;
7902 new->ndup.next = copynode(n->ndup.next);
7903 break;
7904 case NHERE:
7905 case NXHERE:
7906 new->nhere.doc = copynode(n->nhere.doc);
7907 new->nhere.fd = n->nhere.fd;
7908 new->nhere.next = copynode(n->nhere.next);
7909 break;
7910 case NNOT:
7911 new->nnot.com = copynode(n->nnot.com);
7912 break;
7913 };
7914 new->type = n->type;
7915 return new;
7916}
7917
7918/*
7919 * Make a copy of a parse tree.
7920 */
7921static struct funcnode *
7922copyfunc(union node *n)
7923{
7924 struct funcnode *f;
7925 size_t blocksize;
7926
7927 funcblocksize = offsetof(struct funcnode, n);
7928 funcstringsize = 0;
7929 calcsize(n);
7930 blocksize = funcblocksize;
7931 f = ckmalloc(blocksize + funcstringsize);
7932 funcblock = (char *) f + offsetof(struct funcnode, n);
7933 funcstring = (char *) f + blocksize;
7934 copynode(n);
7935 f->count = 0;
7936 return f;
7937}
7938
7939/*
7940 * Define a shell function.
7941 */
7942static void
7943defun(char *name, union node *func)
7944{
7945 struct cmdentry entry;
7946
7947 INT_OFF;
7948 entry.cmdtype = CMDFUNCTION;
7949 entry.u.func = copyfunc(func);
7950 addcmdentry(name, &entry);
7951 INT_ON;
7952}
7953
Denis Vlasenko4b875702009-03-19 13:30:04 +00007954/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007955#define SKIPBREAK (1 << 0)
7956#define SKIPCONT (1 << 1)
7957#define SKIPFUNC (1 << 2)
7958#define SKIPFILE (1 << 3)
7959#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00007960static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007961static int skipcount; /* number of levels to skip */
7962static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007963static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007964
Denis Vlasenko4b875702009-03-19 13:30:04 +00007965/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007966static int evalstring(char *s, int mask);
7967
Denis Vlasenko4b875702009-03-19 13:30:04 +00007968/* Called to execute a trap.
7969 * Single callsite - at the end of evaltree().
7970 * If we return non-zero, exaltree raises EXEXIT exception.
7971 *
7972 * Perhaps we should avoid entering new trap handlers
7973 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007974 */
7975static int
7976dotrap(void)
7977{
Denis Vlasenko4b875702009-03-19 13:30:04 +00007978 uint8_t *g;
7979 int sig;
7980 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007981
7982 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007983 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007984 xbarrier();
7985
Denis Vlasenko653d8e72009-03-19 21:59:35 +00007986 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00007987 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
7988 int want_exexit;
7989 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007990
Denis Vlasenko4b875702009-03-19 13:30:04 +00007991 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007992 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00007993 t = trap[sig];
7994 /* non-trapped SIGINT is handled separately by raise_interrupt,
7995 * don't upset it by resetting gotsig[SIGINT-1] */
7996 if (sig == SIGINT && !t)
7997 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00007998
7999 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008000 *g = 0;
8001 if (!t)
8002 continue;
8003 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008004 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008005 if (want_exexit) {
8006 TRACE(("dotrap returns %d\n", skip));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008007 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008008 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008009 }
8010
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008011 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008012 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008013}
8014
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008015/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00008016static void evalloop(union node *, int);
8017static void evalfor(union node *, int);
8018static void evalcase(union node *, int);
8019static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008020static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00008021static void evalpipe(union node *, int);
8022static void evalcommand(union node *, int);
8023static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008024static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008025
Eric Andersen62483552001-07-10 06:09:16 +00008026/*
Eric Andersenc470f442003-07-28 09:56:35 +00008027 * Evaluate a parse tree. The value is left in the global variable
8028 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008029 */
Eric Andersenc470f442003-07-28 09:56:35 +00008030static void
8031evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008032{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008033 struct jmploc *volatile savehandler = exception_handler;
8034 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008035 int checkexit = 0;
8036 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008037 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008038 int int_level;
8039
8040 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008041
Eric Andersenc470f442003-07-28 09:56:35 +00008042 if (n == NULL) {
8043 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008044 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008045 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008046 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008047
8048 exception_handler = &jmploc;
8049 {
8050 int err = setjmp(jmploc.loc);
8051 if (err) {
8052 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008053 if (exception_type == EXSIG) {
8054 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008055 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008056 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008057 /* continue on the way out */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008058 TRACE(("exception %d in evaltree, propagating err=%d\n", exception, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008059 exception_handler = savehandler;
8060 longjmp(exception_handler->loc, err);
8061 }
8062 }
8063
Eric Andersenc470f442003-07-28 09:56:35 +00008064 switch (n->type) {
8065 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008066#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008067 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008068 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008069 break;
8070#endif
8071 case NNOT:
8072 evaltree(n->nnot.com, EV_TESTED);
8073 status = !exitstatus;
8074 goto setstatus;
8075 case NREDIR:
8076 expredir(n->nredir.redirect);
8077 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8078 if (!status) {
8079 evaltree(n->nredir.n, flags & EV_TESTED);
8080 status = exitstatus;
8081 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008082 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008083 goto setstatus;
8084 case NCMD:
8085 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008086 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008087 if (eflag && !(flags & EV_TESTED))
8088 checkexit = ~0;
8089 goto calleval;
8090 case NFOR:
8091 evalfn = evalfor;
8092 goto calleval;
8093 case NWHILE:
8094 case NUNTIL:
8095 evalfn = evalloop;
8096 goto calleval;
8097 case NSUBSHELL:
8098 case NBACKGND:
8099 evalfn = evalsubshell;
8100 goto calleval;
8101 case NPIPE:
8102 evalfn = evalpipe;
8103 goto checkexit;
8104 case NCASE:
8105 evalfn = evalcase;
8106 goto calleval;
8107 case NAND:
8108 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008109 case NSEMI: {
8110
Eric Andersenc470f442003-07-28 09:56:35 +00008111#if NAND + 1 != NOR
8112#error NAND + 1 != NOR
8113#endif
8114#if NOR + 1 != NSEMI
8115#error NOR + 1 != NSEMI
8116#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008117 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008118 evaltree(
8119 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008120 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008121 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008122 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008123 break;
8124 if (!evalskip) {
8125 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008126 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008127 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008128 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008129 evalfn(n, flags);
8130 break;
8131 }
8132 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008133 }
Eric Andersenc470f442003-07-28 09:56:35 +00008134 case NIF:
8135 evaltree(n->nif.test, EV_TESTED);
8136 if (evalskip)
8137 break;
8138 if (exitstatus == 0) {
8139 n = n->nif.ifpart;
8140 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008141 }
8142 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008143 n = n->nif.elsepart;
8144 goto evaln;
8145 }
8146 goto success;
8147 case NDEFUN:
8148 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008149 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008150 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008151 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008152 exitstatus = status;
8153 break;
8154 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008155
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008156 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008157 exception_handler = savehandler;
8158 out1:
8159 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008160 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008161 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008162 goto exexit;
8163
8164 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008165 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008166 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008167 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008168
8169 RESTORE_INT(int_level);
8170 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008171}
8172
Eric Andersenc470f442003-07-28 09:56:35 +00008173#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8174static
8175#endif
8176void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8177
Eric Andersenc470f442003-07-28 09:56:35 +00008178static void
8179evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008180{
8181 int status;
8182
8183 loopnest++;
8184 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008185 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008186 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008187 int i;
8188
Eric Andersencb57d552001-06-28 07:25:16 +00008189 evaltree(n->nbinary.ch1, EV_TESTED);
8190 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008191 skipping:
8192 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008193 evalskip = 0;
8194 continue;
8195 }
8196 if (evalskip == SKIPBREAK && --skipcount <= 0)
8197 evalskip = 0;
8198 break;
8199 }
Eric Andersenc470f442003-07-28 09:56:35 +00008200 i = exitstatus;
8201 if (n->type != NWHILE)
8202 i = !i;
8203 if (i != 0)
8204 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008205 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008206 status = exitstatus;
8207 if (evalskip)
8208 goto skipping;
8209 }
8210 loopnest--;
8211 exitstatus = status;
8212}
8213
Eric Andersenc470f442003-07-28 09:56:35 +00008214static void
8215evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008216{
8217 struct arglist arglist;
8218 union node *argp;
8219 struct strlist *sp;
8220 struct stackmark smark;
8221
8222 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008223 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008224 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008225 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008226 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008227 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008228 if (evalskip)
8229 goto out;
8230 }
8231 *arglist.lastp = NULL;
8232
8233 exitstatus = 0;
8234 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008235 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008236 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008237 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008238 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008239 if (evalskip) {
8240 if (evalskip == SKIPCONT && --skipcount <= 0) {
8241 evalskip = 0;
8242 continue;
8243 }
8244 if (evalskip == SKIPBREAK && --skipcount <= 0)
8245 evalskip = 0;
8246 break;
8247 }
8248 }
8249 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008250 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008251 popstackmark(&smark);
8252}
8253
Eric Andersenc470f442003-07-28 09:56:35 +00008254static void
8255evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008256{
8257 union node *cp;
8258 union node *patp;
8259 struct arglist arglist;
8260 struct stackmark smark;
8261
8262 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008263 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008264 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008265 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008266 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008267 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8268 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008269 if (casematch(patp, arglist.list->text)) {
8270 if (evalskip == 0) {
8271 evaltree(cp->nclist.body, flags);
8272 }
8273 goto out;
8274 }
8275 }
8276 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008277 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008278 popstackmark(&smark);
8279}
8280
Eric Andersenc470f442003-07-28 09:56:35 +00008281/*
8282 * Kick off a subshell to evaluate a tree.
8283 */
Eric Andersenc470f442003-07-28 09:56:35 +00008284static void
8285evalsubshell(union node *n, int flags)
8286{
8287 struct job *jp;
8288 int backgnd = (n->type == NBACKGND);
8289 int status;
8290
8291 expredir(n->nredir.redirect);
8292 if (!backgnd && flags & EV_EXIT && !trap[0])
8293 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008294 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008295 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008296 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008297 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008298 flags |= EV_EXIT;
8299 if (backgnd)
8300 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008301 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008302 redirect(n->nredir.redirect, 0);
8303 evaltreenr(n->nredir.n, flags);
8304 /* never returns */
8305 }
8306 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008307 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008308 status = waitforjob(jp);
8309 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008310 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008311}
8312
Eric Andersenc470f442003-07-28 09:56:35 +00008313/*
8314 * Compute the names of the files in a redirection list.
8315 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008316static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008317static void
8318expredir(union node *n)
8319{
8320 union node *redir;
8321
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008322 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008323 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008324
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008325 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008326 fn.lastp = &fn.list;
8327 switch (redir->type) {
8328 case NFROMTO:
8329 case NFROM:
8330 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008331#if ENABLE_ASH_BASH_COMPAT
8332 case NTO2:
8333#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008334 case NCLOBBER:
8335 case NAPPEND:
8336 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008337#if ENABLE_ASH_BASH_COMPAT
8338 store_expfname:
8339#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008340 redir->nfile.expfname = fn.list->text;
8341 break;
8342 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008343 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008344 if (redir->ndup.vname) {
8345 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008346 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008347 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008348#if ENABLE_ASH_BASH_COMPAT
8349//FIXME: we used expandarg with different args!
8350 if (!isdigit_str9(fn.list->text)) {
8351 /* >&file, not >&fd */
8352 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8353 ash_msg_and_raise_error("redir error");
8354 redir->type = NTO2;
8355 goto store_expfname;
8356 }
8357#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008358 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008359 }
8360 break;
8361 }
8362 }
8363}
8364
Eric Andersencb57d552001-06-28 07:25:16 +00008365/*
Eric Andersencb57d552001-06-28 07:25:16 +00008366 * Evaluate a pipeline. All the processes in the pipeline are children
8367 * of the process creating the pipeline. (This differs from some versions
8368 * of the shell, which make the last process in a pipeline the parent
8369 * of all the rest.)
8370 */
Eric Andersenc470f442003-07-28 09:56:35 +00008371static void
8372evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008373{
8374 struct job *jp;
8375 struct nodelist *lp;
8376 int pipelen;
8377 int prevfd;
8378 int pip[2];
8379
Eric Andersenc470f442003-07-28 09:56:35 +00008380 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008381 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008382 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008383 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008384 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008385 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008386 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008387 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008388 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008389 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008390 pip[1] = -1;
8391 if (lp->next) {
8392 if (pipe(pip) < 0) {
8393 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008394 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008395 }
8396 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008397 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008398 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008399 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008400 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008401 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008402 if (prevfd > 0) {
8403 dup2(prevfd, 0);
8404 close(prevfd);
8405 }
8406 if (pip[1] > 1) {
8407 dup2(pip[1], 1);
8408 close(pip[1]);
8409 }
Eric Andersenc470f442003-07-28 09:56:35 +00008410 evaltreenr(lp->n, flags);
8411 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008412 }
8413 if (prevfd >= 0)
8414 close(prevfd);
8415 prevfd = pip[0];
8416 close(pip[1]);
8417 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008418 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008419 exitstatus = waitforjob(jp);
8420 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008421 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008422 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008423}
8424
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008425/*
8426 * Controls whether the shell is interactive or not.
8427 */
8428static void
8429setinteractive(int on)
8430{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008431 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008432
8433 if (++on == is_interactive)
8434 return;
8435 is_interactive = on;
8436 setsignal(SIGINT);
8437 setsignal(SIGQUIT);
8438 setsignal(SIGTERM);
8439#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8440 if (is_interactive > 1) {
8441 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008442 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008443
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008444 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008445 out1fmt(
8446 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008447 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008448 "Enter 'help' for a list of built-in commands."
8449 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008450 bb_banner);
8451 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008452 }
8453 }
8454#endif
8455}
8456
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008457static void
8458optschanged(void)
8459{
8460#if DEBUG
8461 opentrace();
8462#endif
8463 setinteractive(iflag);
8464 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008465#if ENABLE_FEATURE_EDITING_VI
8466 if (viflag)
8467 line_input_state->flags |= VI_MODE;
8468 else
8469 line_input_state->flags &= ~VI_MODE;
8470#else
8471 viflag = 0; /* forcibly keep the option off */
8472#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008473}
8474
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008475static struct localvar *localvars;
8476
8477/*
8478 * Called after a function returns.
8479 * Interrupts must be off.
8480 */
8481static void
8482poplocalvars(void)
8483{
8484 struct localvar *lvp;
8485 struct var *vp;
8486
8487 while ((lvp = localvars) != NULL) {
8488 localvars = lvp->next;
8489 vp = lvp->vp;
8490 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8491 if (vp == NULL) { /* $- saved */
8492 memcpy(optlist, lvp->text, sizeof(optlist));
8493 free((char*)lvp->text);
8494 optschanged();
8495 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8496 unsetvar(vp->text);
8497 } else {
8498 if (vp->func)
8499 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8500 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8501 free((char*)vp->text);
8502 vp->flags = lvp->flags;
8503 vp->text = lvp->text;
8504 }
8505 free(lvp);
8506 }
8507}
8508
8509static int
8510evalfun(struct funcnode *func, int argc, char **argv, int flags)
8511{
8512 volatile struct shparam saveparam;
8513 struct localvar *volatile savelocalvars;
8514 struct jmploc *volatile savehandler;
8515 struct jmploc jmploc;
8516 int e;
8517
8518 saveparam = shellparam;
8519 savelocalvars = localvars;
8520 e = setjmp(jmploc.loc);
8521 if (e) {
8522 goto funcdone;
8523 }
8524 INT_OFF;
8525 savehandler = exception_handler;
8526 exception_handler = &jmploc;
8527 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008528 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008529 func->count++;
8530 funcnest++;
8531 INT_ON;
8532 shellparam.nparam = argc - 1;
8533 shellparam.p = argv + 1;
8534#if ENABLE_ASH_GETOPTS
8535 shellparam.optind = 1;
8536 shellparam.optoff = -1;
8537#endif
8538 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008539 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008540 INT_OFF;
8541 funcnest--;
8542 freefunc(func);
8543 poplocalvars();
8544 localvars = savelocalvars;
8545 freeparam(&shellparam);
8546 shellparam = saveparam;
8547 exception_handler = savehandler;
8548 INT_ON;
8549 evalskip &= ~SKIPFUNC;
8550 return e;
8551}
8552
Denis Vlasenko131ae172007-02-18 13:00:19 +00008553#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008554static char **
8555parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008556{
8557 char *cp, c;
8558
8559 for (;;) {
8560 cp = *++argv;
8561 if (!cp)
8562 return 0;
8563 if (*cp++ != '-')
8564 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008565 c = *cp++;
8566 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008567 break;
8568 if (c == '-' && !*cp) {
8569 argv++;
8570 break;
8571 }
8572 do {
8573 switch (c) {
8574 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008575 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008576 break;
8577 default:
8578 /* run 'typecmd' for other options */
8579 return 0;
8580 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008581 c = *cp++;
8582 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008583 }
8584 return argv;
8585}
8586#endif
8587
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008588/*
8589 * Make a variable a local variable. When a variable is made local, it's
8590 * value and flags are saved in a localvar structure. The saved values
8591 * will be restored when the shell function returns. We handle the name
8592 * "-" as a special case.
8593 */
8594static void
8595mklocal(char *name)
8596{
8597 struct localvar *lvp;
8598 struct var **vpp;
8599 struct var *vp;
8600
8601 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008602 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008603 if (LONE_DASH(name)) {
8604 char *p;
8605 p = ckmalloc(sizeof(optlist));
8606 lvp->text = memcpy(p, optlist, sizeof(optlist));
8607 vp = NULL;
8608 } else {
8609 char *eq;
8610
8611 vpp = hashvar(name);
8612 vp = *findvar(vpp, name);
8613 eq = strchr(name, '=');
8614 if (vp == NULL) {
8615 if (eq)
8616 setvareq(name, VSTRFIXED);
8617 else
8618 setvar(name, NULL, VSTRFIXED);
8619 vp = *vpp; /* the new variable */
8620 lvp->flags = VUNSET;
8621 } else {
8622 lvp->text = vp->text;
8623 lvp->flags = vp->flags;
8624 vp->flags |= VSTRFIXED|VTEXTFIXED;
8625 if (eq)
8626 setvareq(name, 0);
8627 }
8628 }
8629 lvp->vp = vp;
8630 lvp->next = localvars;
8631 localvars = lvp;
8632 INT_ON;
8633}
8634
8635/*
8636 * The "local" command.
8637 */
8638static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008639localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008640{
8641 char *name;
8642
8643 argv = argptr;
8644 while ((name = *argv++) != NULL) {
8645 mklocal(name);
8646 }
8647 return 0;
8648}
8649
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008650static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008651falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008652{
8653 return 1;
8654}
8655
8656static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008657truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008658{
8659 return 0;
8660}
8661
8662static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008663execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008664{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008665 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008666 iflag = 0; /* exit on error */
8667 mflag = 0;
8668 optschanged();
8669 shellexec(argv + 1, pathval(), 0);
8670 }
8671 return 0;
8672}
8673
8674/*
8675 * The return command.
8676 */
8677static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008678returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008679{
8680 /*
8681 * If called outside a function, do what ksh does;
8682 * skip the rest of the file.
8683 */
8684 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8685 return argv[1] ? number(argv[1]) : exitstatus;
8686}
8687
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008688/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008689static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008690static int dotcmd(int, char **);
8691static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008692static int exitcmd(int, char **);
8693static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008694#if ENABLE_ASH_GETOPTS
8695static int getoptscmd(int, char **);
8696#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008697#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008698static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008699#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008700#if ENABLE_ASH_MATH_SUPPORT
8701static int letcmd(int, char **);
8702#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008703static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008704static int setcmd(int, char **);
8705static int shiftcmd(int, char **);
8706static int timescmd(int, char **);
8707static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008708static int umaskcmd(int, char **);
8709static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008710static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008711
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008712#define BUILTIN_NOSPEC "0"
8713#define BUILTIN_SPECIAL "1"
8714#define BUILTIN_REGULAR "2"
8715#define BUILTIN_SPEC_REG "3"
8716#define BUILTIN_ASSIGN "4"
8717#define BUILTIN_SPEC_ASSG "5"
8718#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008719#define BUILTIN_SPEC_REG_ASSG "7"
8720
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008721/* We do not handle [[ expr ]] bashism bash-compatibly,
8722 * we make it a synonym of [ expr ].
8723 * Basically, word splitting and pathname expansion should NOT be performed
8724 * Examples:
8725 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8726 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8727 * Additional operators:
8728 * || and && should work as -o and -a
8729 * =~ regexp match
8730 * Apart from the above, [[ expr ]] should work as [ expr ]
8731 */
8732
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008733#define echocmd echo_main
8734#define printfcmd printf_main
8735#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008736
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008737/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008738static const struct builtincmd builtintab[] = {
8739 { BUILTIN_SPEC_REG ".", dotcmd },
8740 { BUILTIN_SPEC_REG ":", truecmd },
8741#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008742 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008743#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008744 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008745#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008746#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008747#if ENABLE_ASH_ALIAS
8748 { BUILTIN_REG_ASSG "alias", aliascmd },
8749#endif
8750#if JOBS
8751 { BUILTIN_REGULAR "bg", fg_bgcmd },
8752#endif
8753 { BUILTIN_SPEC_REG "break", breakcmd },
8754 { BUILTIN_REGULAR "cd", cdcmd },
8755 { BUILTIN_NOSPEC "chdir", cdcmd },
8756#if ENABLE_ASH_CMDCMD
8757 { BUILTIN_REGULAR "command", commandcmd },
8758#endif
8759 { BUILTIN_SPEC_REG "continue", breakcmd },
8760#if ENABLE_ASH_BUILTIN_ECHO
8761 { BUILTIN_REGULAR "echo", echocmd },
8762#endif
8763 { BUILTIN_SPEC_REG "eval", evalcmd },
8764 { BUILTIN_SPEC_REG "exec", execcmd },
8765 { BUILTIN_SPEC_REG "exit", exitcmd },
8766 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8767 { BUILTIN_REGULAR "false", falsecmd },
8768#if JOBS
8769 { BUILTIN_REGULAR "fg", fg_bgcmd },
8770#endif
8771#if ENABLE_ASH_GETOPTS
8772 { BUILTIN_REGULAR "getopts", getoptscmd },
8773#endif
8774 { BUILTIN_NOSPEC "hash", hashcmd },
8775#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8776 { BUILTIN_NOSPEC "help", helpcmd },
8777#endif
8778#if JOBS
8779 { BUILTIN_REGULAR "jobs", jobscmd },
8780 { BUILTIN_REGULAR "kill", killcmd },
8781#endif
8782#if ENABLE_ASH_MATH_SUPPORT
8783 { BUILTIN_NOSPEC "let", letcmd },
8784#endif
8785 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008786#if ENABLE_ASH_BUILTIN_PRINTF
8787 { BUILTIN_REGULAR "printf", printfcmd },
8788#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008789 { BUILTIN_NOSPEC "pwd", pwdcmd },
8790 { BUILTIN_REGULAR "read", readcmd },
8791 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8792 { BUILTIN_SPEC_REG "return", returncmd },
8793 { BUILTIN_SPEC_REG "set", setcmd },
8794 { BUILTIN_SPEC_REG "shift", shiftcmd },
8795 { BUILTIN_SPEC_REG "source", dotcmd },
8796#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008797 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008798#endif
8799 { BUILTIN_SPEC_REG "times", timescmd },
8800 { BUILTIN_SPEC_REG "trap", trapcmd },
8801 { BUILTIN_REGULAR "true", truecmd },
8802 { BUILTIN_NOSPEC "type", typecmd },
8803 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8804 { BUILTIN_REGULAR "umask", umaskcmd },
8805#if ENABLE_ASH_ALIAS
8806 { BUILTIN_REGULAR "unalias", unaliascmd },
8807#endif
8808 { BUILTIN_SPEC_REG "unset", unsetcmd },
8809 { BUILTIN_REGULAR "wait", waitcmd },
8810};
8811
Denis Vlasenko80591b02008-03-25 07:49:43 +00008812/* Should match the above table! */
8813#define COMMANDCMD (builtintab + \
8814 2 + \
8815 1 * ENABLE_ASH_BUILTIN_TEST + \
8816 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8817 1 * ENABLE_ASH_ALIAS + \
8818 1 * ENABLE_ASH_JOB_CONTROL + \
8819 3)
8820#define EXECCMD (builtintab + \
8821 2 + \
8822 1 * ENABLE_ASH_BUILTIN_TEST + \
8823 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8824 1 * ENABLE_ASH_ALIAS + \
8825 1 * ENABLE_ASH_JOB_CONTROL + \
8826 3 + \
8827 1 * ENABLE_ASH_CMDCMD + \
8828 1 + \
8829 ENABLE_ASH_BUILTIN_ECHO + \
8830 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008831
8832/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008833 * Search the table of builtin commands.
8834 */
8835static struct builtincmd *
8836find_builtin(const char *name)
8837{
8838 struct builtincmd *bp;
8839
8840 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008841 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008842 pstrcmp
8843 );
8844 return bp;
8845}
8846
8847/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008848 * Execute a simple command.
8849 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008850static int
8851isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008852{
8853 const char *q = endofname(p);
8854 if (p == q)
8855 return 0;
8856 return *q == '=';
8857}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008858static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008859bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008860{
8861 /* Preserve exitstatus of a previous possible redirection
8862 * as POSIX mandates */
8863 return back_exitstatus;
8864}
Eric Andersenc470f442003-07-28 09:56:35 +00008865static void
8866evalcommand(union node *cmd, int flags)
8867{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008868 static const struct builtincmd null_bltin = {
8869 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008870 };
Eric Andersenc470f442003-07-28 09:56:35 +00008871 struct stackmark smark;
8872 union node *argp;
8873 struct arglist arglist;
8874 struct arglist varlist;
8875 char **argv;
8876 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008877 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008878 struct cmdentry cmdentry;
8879 struct job *jp;
8880 char *lastarg;
8881 const char *path;
8882 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008883 int status;
8884 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008885 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008886 smallint cmd_is_exec;
8887 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008888
8889 /* First expand the arguments. */
8890 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8891 setstackmark(&smark);
8892 back_exitstatus = 0;
8893
8894 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008895 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008896 varlist.lastp = &varlist.list;
8897 *varlist.lastp = NULL;
8898 arglist.lastp = &arglist.list;
8899 *arglist.lastp = NULL;
8900
8901 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008902 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008903 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8904 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8905 }
8906
Eric Andersenc470f442003-07-28 09:56:35 +00008907 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8908 struct strlist **spp;
8909
8910 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008911 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008912 expandarg(argp, &arglist, EXP_VARTILDE);
8913 else
8914 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8915
Eric Andersenc470f442003-07-28 09:56:35 +00008916 for (sp = *spp; sp; sp = sp->next)
8917 argc++;
8918 }
8919
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008920 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008921 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008922 TRACE(("evalcommand arg: %s\n", sp->text));
8923 *nargv++ = sp->text;
8924 }
8925 *nargv = NULL;
8926
8927 lastarg = NULL;
8928 if (iflag && funcnest == 0 && argc > 0)
8929 lastarg = nargv[-1];
8930
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008931 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008932 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008933 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008934
8935 path = vpath.text;
8936 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8937 struct strlist **spp;
8938 char *p;
8939
8940 spp = varlist.lastp;
8941 expandarg(argp, &varlist, EXP_VARTILDE);
8942
8943 /*
8944 * Modify the command lookup path, if a PATH= assignment
8945 * is present
8946 */
8947 p = (*spp)->text;
8948 if (varequal(p, path))
8949 path = p;
8950 }
8951
8952 /* Print the command if xflag is set. */
8953 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008954 int n;
8955 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008956
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008957 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008958 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008959
8960 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008961 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008962 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008963 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008964 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008965 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008966 p--;
8967 }
8968 }
8969 sp = arglist.list;
8970 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008971 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008972 }
8973
8974 cmd_is_exec = 0;
8975 spclbltin = -1;
8976
8977 /* Now locate the command. */
8978 if (argc) {
8979 const char *oldpath;
8980 int cmd_flag = DO_ERR;
8981
8982 path += 5;
8983 oldpath = path;
8984 for (;;) {
8985 find_command(argv[0], &cmdentry, cmd_flag, path);
8986 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008987 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008988 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008989 goto bail;
8990 }
8991
8992 /* implement bltin and command here */
8993 if (cmdentry.cmdtype != CMDBUILTIN)
8994 break;
8995 if (spclbltin < 0)
8996 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
8997 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008998 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00008999#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009000 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009001 path = oldpath;
9002 nargv = parse_command_args(argv, &path);
9003 if (!nargv)
9004 break;
9005 argc -= nargv - argv;
9006 argv = nargv;
9007 cmd_flag |= DO_NOFUNC;
9008 } else
9009#endif
9010 break;
9011 }
9012 }
9013
9014 if (status) {
9015 /* We have a redirection error. */
9016 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009017 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009018 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009019 exitstatus = status;
9020 goto out;
9021 }
9022
9023 /* Execute the command. */
9024 switch (cmdentry.cmdtype) {
9025 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009026
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009027#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009028/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
9029 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009030 {
9031 /* find_command() encodes applet_no as (-2 - applet_no) */
9032 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009033 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009034 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009035 /* run <applet>_main() */
9036 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009037 break;
9038 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009039 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009040#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009041 /* Fork off a child process if necessary. */
9042 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00009043 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009044 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009045 if (forkshell(jp, cmd, FORK_FG) != 0) {
9046 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009047 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009048 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009049 break;
9050 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009051 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009052 }
9053 listsetvar(varlist.list, VEXPORT|VSTACK);
9054 shellexec(argv, path, cmdentry.u.index);
9055 /* NOTREACHED */
9056
9057 case CMDBUILTIN:
9058 cmdenviron = varlist.list;
9059 if (cmdenviron) {
9060 struct strlist *list = cmdenviron;
9061 int i = VNOSET;
9062 if (spclbltin > 0 || argc == 0) {
9063 i = 0;
9064 if (cmd_is_exec && argc > 1)
9065 i = VEXPORT;
9066 }
9067 listsetvar(list, i);
9068 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009069 /* Tight loop with builtins only:
9070 * "while kill -0 $child; do true; done"
9071 * will never exit even if $child died, unless we do this
9072 * to reap the zombie and make kill detect that it's gone: */
9073 dowait(DOWAIT_NONBLOCK, NULL);
9074
Eric Andersenc470f442003-07-28 09:56:35 +00009075 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9076 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009077 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009078 if (i == EXEXIT)
9079 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009080 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009081 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009082 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009083 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009084 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009085 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009086 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009087 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009088 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009089 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009090 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009091 }
9092 break;
9093
9094 case CMDFUNCTION:
9095 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009096 /* See above for the rationale */
9097 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009098 if (evalfun(cmdentry.u.func, argc, argv, flags))
9099 goto raise;
9100 break;
9101 }
9102
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009103 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009104 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009105 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009106 /* dsl: I think this is intended to be used to support
9107 * '_' in 'vi' command mode during line editing...
9108 * However I implemented that within libedit itself.
9109 */
9110 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009111 }
Eric Andersenc470f442003-07-28 09:56:35 +00009112 popstackmark(&smark);
9113}
9114
9115static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009116evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9117{
Eric Andersenc470f442003-07-28 09:56:35 +00009118 char *volatile savecmdname;
9119 struct jmploc *volatile savehandler;
9120 struct jmploc jmploc;
9121 int i;
9122
9123 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009124 i = setjmp(jmploc.loc);
9125 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009126 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009127 savehandler = exception_handler;
9128 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009129 commandname = argv[0];
9130 argptr = argv + 1;
9131 optptr = NULL; /* initialize nextopt */
9132 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009133 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009134 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009135 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009136 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009137 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009138// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009139 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009140
9141 return i;
9142}
9143
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009144static int
9145goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009146{
9147 return !*endofname(p);
9148}
9149
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009150
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009151/*
9152 * Search for a command. This is called before we fork so that the
9153 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009154 * the child. The check for "goodname" is an overly conservative
9155 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009156 */
Eric Andersenc470f442003-07-28 09:56:35 +00009157static void
9158prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009159{
9160 struct cmdentry entry;
9161
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009162 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9163 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009164}
9165
Eric Andersencb57d552001-06-28 07:25:16 +00009166
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009167/* ============ Builtin commands
9168 *
9169 * Builtin commands whose functions are closely tied to evaluation
9170 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009171 */
9172
9173/*
Eric Andersencb57d552001-06-28 07:25:16 +00009174 * Handle break and continue commands. Break, continue, and return are
9175 * all handled by setting the evalskip flag. The evaluation routines
9176 * above all check this flag, and if it is set they start skipping
9177 * commands rather than executing them. The variable skipcount is
9178 * the number of loops to break/continue, or the number of function
9179 * levels to return. (The latter is always 1.) It should probably
9180 * be an error to break out of more loops than exist, but it isn't
9181 * in the standard shell so we don't make it one here.
9182 */
Eric Andersenc470f442003-07-28 09:56:35 +00009183static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009184breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009185{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009186 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009187
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009188 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009189 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009190 if (n > loopnest)
9191 n = loopnest;
9192 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009193 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009194 skipcount = n;
9195 }
9196 return 0;
9197}
9198
Eric Andersenc470f442003-07-28 09:56:35 +00009199
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009200/* ============ input.c
9201 *
Eric Andersen90898442003-08-06 11:20:52 +00009202 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009203 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009204
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009205enum {
9206 INPUT_PUSH_FILE = 1,
9207 INPUT_NOFILE_OK = 2,
9208};
Eric Andersencb57d552001-06-28 07:25:16 +00009209
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009210static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009211/* values of checkkwd variable */
9212#define CHKALIAS 0x1
9213#define CHKKWD 0x2
9214#define CHKNL 0x4
9215
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009216/*
9217 * Push a string back onto the input at this current parsefile level.
9218 * We handle aliases this way.
9219 */
9220#if !ENABLE_ASH_ALIAS
9221#define pushstring(s, ap) pushstring(s)
9222#endif
9223static void
9224pushstring(char *s, struct alias *ap)
9225{
9226 struct strpush *sp;
9227 int len;
9228
9229 len = strlen(s);
9230 INT_OFF;
9231 if (g_parsefile->strpush) {
9232 sp = ckzalloc(sizeof(*sp));
9233 sp->prev = g_parsefile->strpush;
9234 } else {
9235 sp = &(g_parsefile->basestrpush);
9236 }
9237 g_parsefile->strpush = sp;
9238 sp->prev_string = g_parsefile->next_to_pgetc;
9239 sp->prev_left_in_line = g_parsefile->left_in_line;
9240#if ENABLE_ASH_ALIAS
9241 sp->ap = ap;
9242 if (ap) {
9243 ap->flag |= ALIASINUSE;
9244 sp->string = s;
9245 }
9246#endif
9247 g_parsefile->next_to_pgetc = s;
9248 g_parsefile->left_in_line = len;
9249 INT_ON;
9250}
9251
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009252static void
9253popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009254{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009255 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009256
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009257 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009258#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009259 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009260 if (g_parsefile->next_to_pgetc[-1] == ' '
9261 || g_parsefile->next_to_pgetc[-1] == '\t'
9262 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009263 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009264 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009265 if (sp->string != sp->ap->val) {
9266 free(sp->string);
9267 }
9268 sp->ap->flag &= ~ALIASINUSE;
9269 if (sp->ap->flag & ALIASDEAD) {
9270 unalias(sp->ap->name);
9271 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009272 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009273#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009274 g_parsefile->next_to_pgetc = sp->prev_string;
9275 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009276 g_parsefile->strpush = sp->prev;
9277 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009278 free(sp);
9279 INT_ON;
9280}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009281
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009282//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9283//it peeks whether it is &>, and then pushes back both chars.
9284//This function needs to save last *next_to_pgetc to buf[0]
9285//to make two pungetc() reliable. Currently,
9286// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009287static int
9288preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009289{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009290 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009291 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009292
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009293 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009294#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009295 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009296 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009297 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009298 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009299#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009300 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009301#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009302 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9303 if (nr == 0) {
9304 /* Ctrl+C pressed */
9305 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009306 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009307 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009308 raise(SIGINT);
9309 return 1;
9310 }
Eric Andersenc470f442003-07-28 09:56:35 +00009311 goto retry;
9312 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009313 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009314 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009315 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009316 }
Eric Andersencb57d552001-06-28 07:25:16 +00009317 }
9318#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009319 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009320#endif
9321
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009322#if 0
9323/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009324 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009325 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009326 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009327 if (flags >= 0 && (flags & O_NONBLOCK)) {
9328 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009329 if (fcntl(0, F_SETFL, flags) >= 0) {
9330 out2str("sh: turning off NDELAY mode\n");
9331 goto retry;
9332 }
9333 }
9334 }
9335 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009336#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009337 return nr;
9338}
9339
9340/*
9341 * Refill the input buffer and return the next input character:
9342 *
9343 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009344 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9345 * or we are reading from a string so we can't refill the buffer,
9346 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009347 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9348 * 4) Process input up to the next newline, deleting nul characters.
9349 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009350//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9351#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009352/*
9353 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9354 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9355 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9356 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9357 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009358static int
Eric Andersenc470f442003-07-28 09:56:35 +00009359preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009360{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009361 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009362 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009363
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009364 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009365#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009366 if (g_parsefile->left_in_line == -1
9367 && g_parsefile->strpush->ap
9368 && g_parsefile->next_to_pgetc[-1] != ' '
9369 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009370 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009371 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009372 return PEOA;
9373 }
Eric Andersen2870d962001-07-02 17:27:21 +00009374#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009375 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009376 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009377 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9378 g_parsefile->left_in_line,
9379 g_parsefile->next_to_pgetc,
9380 g_parsefile->next_to_pgetc);
9381 if (--g_parsefile->left_in_line >= 0)
9382 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009383 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009384 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009385 * "pgetc" needs refilling.
9386 */
9387
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009388 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009389 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009390 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009391 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009392 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009393 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009394 /* even in failure keep left_in_line and next_to_pgetc
9395 * in lock step, for correct multi-layer pungetc.
9396 * left_in_line was decremented before preadbuffer(),
9397 * must inc next_to_pgetc: */
9398 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009399 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009400 }
Eric Andersencb57d552001-06-28 07:25:16 +00009401
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009402 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009403 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009404 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009405 again:
9406 more = preadfd();
9407 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009408 /* don't try reading again */
9409 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009410 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009411 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009412 return PEOF;
9413 }
9414 }
9415
Denis Vlasenko727752d2008-11-28 03:41:47 +00009416 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009417 * Set g_parsefile->left_in_line
9418 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009419 * NUL chars are deleted.
9420 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009421 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009422 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009423 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009424
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009425 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009426
Denis Vlasenko727752d2008-11-28 03:41:47 +00009427 c = *q;
9428 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009429 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009430 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009431 q++;
9432 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009433 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009434 break;
9435 }
Eric Andersencb57d552001-06-28 07:25:16 +00009436 }
9437
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009438 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009439 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9440 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009441 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009442 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009443 }
9444 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009445 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009446
Eric Andersencb57d552001-06-28 07:25:16 +00009447 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009448 char save = *q;
9449 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009450 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009451 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009452 }
9453
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009454 pgetc_debug("preadbuffer at %d:%p'%s'",
9455 g_parsefile->left_in_line,
9456 g_parsefile->next_to_pgetc,
9457 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009458 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009459}
9460
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009461#define pgetc_as_macro() \
9462 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009463 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009464 : preadbuffer() \
9465 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009466
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009467static int
9468pgetc(void)
9469{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009470 pgetc_debug("pgetc_fast at %d:%p'%s'",
9471 g_parsefile->left_in_line,
9472 g_parsefile->next_to_pgetc,
9473 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009474 return pgetc_as_macro();
9475}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009476
9477#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009478#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009479#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009480#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009481#endif
9482
9483/*
9484 * Same as pgetc(), but ignores PEOA.
9485 */
9486#if ENABLE_ASH_ALIAS
9487static int
9488pgetc2(void)
9489{
9490 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009491 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009492 pgetc_debug("pgetc_fast at %d:%p'%s'",
9493 g_parsefile->left_in_line,
9494 g_parsefile->next_to_pgetc,
9495 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009496 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009497 } while (c == PEOA);
9498 return c;
9499}
9500#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009501#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009502#endif
9503
9504/*
9505 * Read a line from the script.
9506 */
9507static char *
9508pfgets(char *line, int len)
9509{
9510 char *p = line;
9511 int nleft = len;
9512 int c;
9513
9514 while (--nleft > 0) {
9515 c = pgetc2();
9516 if (c == PEOF) {
9517 if (p == line)
9518 return NULL;
9519 break;
9520 }
9521 *p++ = c;
9522 if (c == '\n')
9523 break;
9524 }
9525 *p = '\0';
9526 return line;
9527}
9528
Eric Andersenc470f442003-07-28 09:56:35 +00009529/*
9530 * Undo the last call to pgetc. Only one character may be pushed back.
9531 * PEOF may be pushed back.
9532 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009533static void
Eric Andersenc470f442003-07-28 09:56:35 +00009534pungetc(void)
9535{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009536 g_parsefile->left_in_line++;
9537 g_parsefile->next_to_pgetc--;
9538 pgetc_debug("pushed back to %d:%p'%s'",
9539 g_parsefile->left_in_line,
9540 g_parsefile->next_to_pgetc,
9541 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009542}
9543
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009544/*
9545 * To handle the "." command, a stack of input files is used. Pushfile
9546 * adds a new entry to the stack and popfile restores the previous level.
9547 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009548static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009549pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009550{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009551 struct parsefile *pf;
9552
Denis Vlasenko597906c2008-02-20 16:38:54 +00009553 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009554 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009555 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009556 /*pf->strpush = NULL; - ckzalloc did it */
9557 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009558 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009559}
9560
9561static void
9562popfile(void)
9563{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009564 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009565
Denis Vlasenkob012b102007-02-19 22:43:01 +00009566 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009567 if (pf->fd >= 0)
9568 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009569 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009570 while (pf->strpush)
9571 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009572 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009573 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009574 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009575}
9576
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009577/*
9578 * Return to top level.
9579 */
9580static void
9581popallfiles(void)
9582{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009583 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009584 popfile();
9585}
9586
9587/*
9588 * Close the file(s) that the shell is reading commands from. Called
9589 * after a fork is done.
9590 */
9591static void
9592closescript(void)
9593{
9594 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009595 if (g_parsefile->fd > 0) {
9596 close(g_parsefile->fd);
9597 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009598 }
9599}
9600
9601/*
9602 * Like setinputfile, but takes an open file descriptor. Call this with
9603 * interrupts off.
9604 */
9605static void
9606setinputfd(int fd, int push)
9607{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009608 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009609 if (push) {
9610 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009611 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009612 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009613 g_parsefile->fd = fd;
9614 if (g_parsefile->buf == NULL)
9615 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009616 g_parsefile->left_in_buffer = 0;
9617 g_parsefile->left_in_line = 0;
9618 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009619}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009620
Eric Andersenc470f442003-07-28 09:56:35 +00009621/*
9622 * Set the input to take input from a file. If push is set, push the
9623 * old input onto the stack first.
9624 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009625static int
9626setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009627{
9628 int fd;
9629 int fd2;
9630
Denis Vlasenkob012b102007-02-19 22:43:01 +00009631 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009632 fd = open(fname, O_RDONLY);
9633 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009634 if (flags & INPUT_NOFILE_OK)
9635 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009636 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009637 }
Eric Andersenc470f442003-07-28 09:56:35 +00009638 if (fd < 10) {
9639 fd2 = copyfd(fd, 10);
9640 close(fd);
9641 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009642 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009643 fd = fd2;
9644 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009645 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009646 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009647 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009648 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009649}
9650
Eric Andersencb57d552001-06-28 07:25:16 +00009651/*
9652 * Like setinputfile, but takes input from a string.
9653 */
Eric Andersenc470f442003-07-28 09:56:35 +00009654static void
9655setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009656{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009657 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009658 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009659 g_parsefile->next_to_pgetc = string;
9660 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009661 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009662 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009663 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009664}
9665
9666
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009667/* ============ mail.c
9668 *
9669 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009670 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009671
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009672#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009673
Eric Andersencb57d552001-06-28 07:25:16 +00009674#define MAXMBOXES 10
9675
Eric Andersenc470f442003-07-28 09:56:35 +00009676/* times of mailboxes */
9677static time_t mailtime[MAXMBOXES];
9678/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009679static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009680
Eric Andersencb57d552001-06-28 07:25:16 +00009681/*
Eric Andersenc470f442003-07-28 09:56:35 +00009682 * Print appropriate message(s) if mail has arrived.
9683 * If mail_var_path_changed is set,
9684 * then the value of MAIL has mail_var_path_changed,
9685 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009686 */
Eric Andersenc470f442003-07-28 09:56:35 +00009687static void
9688chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009689{
Eric Andersencb57d552001-06-28 07:25:16 +00009690 const char *mpath;
9691 char *p;
9692 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009693 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009694 struct stackmark smark;
9695 struct stat statb;
9696
Eric Andersencb57d552001-06-28 07:25:16 +00009697 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009698 mpath = mpathset() ? mpathval() : mailval();
9699 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009700 p = padvance(&mpath, nullstr);
9701 if (p == NULL)
9702 break;
9703 if (*p == '\0')
9704 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009705 for (q = p; *q; q++)
9706 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009707#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009708 if (q[-1] != '/')
9709 abort();
9710#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009711 q[-1] = '\0'; /* delete trailing '/' */
9712 if (stat(p, &statb) < 0) {
9713 *mtp = 0;
9714 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009715 }
Eric Andersenc470f442003-07-28 09:56:35 +00009716 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9717 fprintf(
9718 stderr, snlfmt,
9719 pathopt ? pathopt : "you have mail"
9720 );
9721 }
9722 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
Eric Andersenc470f442003-07-28 09:56:35 +00009724 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009725 popstackmark(&smark);
9726}
Eric Andersencb57d552001-06-28 07:25:16 +00009727
Eric Andersenc470f442003-07-28 09:56:35 +00009728static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009729changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009730{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009731 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009732}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009733
Denis Vlasenko131ae172007-02-18 13:00:19 +00009734#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009735
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009736
9737/* ============ ??? */
9738
Eric Andersencb57d552001-06-28 07:25:16 +00009739/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009740 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009741 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009742static void
9743setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009744{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009745 char **newparam;
9746 char **ap;
9747 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009748
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009749 for (nparam = 0; argv[nparam]; nparam++)
9750 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009751 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9752 while (*argv) {
9753 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009754 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009755 *ap = NULL;
9756 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009757 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009758 shellparam.nparam = nparam;
9759 shellparam.p = newparam;
9760#if ENABLE_ASH_GETOPTS
9761 shellparam.optind = 1;
9762 shellparam.optoff = -1;
9763#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009764}
9765
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009766/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009767 * Process shell options. The global variable argptr contains a pointer
9768 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009769 *
9770 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9771 * For a non-interactive shell, an error condition encountered
9772 * by a special built-in ... shall cause the shell to write a diagnostic message
9773 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009774 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009775 * ...
9776 * Utility syntax error (option or operand error) Shall exit
9777 * ...
9778 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9779 * we see that bash does not do that (set "finishes" with error code 1 instead,
9780 * and shell continues), and people rely on this behavior!
9781 * Testcase:
9782 * set -o barfoo 2>/dev/null
9783 * echo $?
9784 *
9785 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009786 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009787static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009788plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009789{
9790 int i;
9791
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009792 if (name) {
9793 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009794 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009795 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009796 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009797 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009798 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009799 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009800 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009801 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009802 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009803 if (val) {
9804 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9805 } else {
9806 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9807 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009808 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009809 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009810}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009811static void
9812setoption(int flag, int val)
9813{
9814 int i;
9815
9816 for (i = 0; i < NOPTS; i++) {
9817 if (optletters(i) == flag) {
9818 optlist[i] = val;
9819 return;
9820 }
9821 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009822 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009823 /* NOTREACHED */
9824}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009825static int
Eric Andersenc470f442003-07-28 09:56:35 +00009826options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009827{
9828 char *p;
9829 int val;
9830 int c;
9831
9832 if (cmdline)
9833 minusc = NULL;
9834 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009835 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009836 if (c != '-' && c != '+')
9837 break;
9838 argptr++;
9839 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009840 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009841 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009842 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009843 if (!cmdline) {
9844 /* "-" means turn off -x and -v */
9845 if (p[0] == '\0')
9846 xflag = vflag = 0;
9847 /* "--" means reset params */
9848 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009849 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009850 }
Eric Andersenc470f442003-07-28 09:56:35 +00009851 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009852 }
Eric Andersencb57d552001-06-28 07:25:16 +00009853 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009854 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009855 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009856 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009857 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009858 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009859 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009860 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009861 /* it already printed err message */
9862 return 1; /* error */
9863 }
Eric Andersencb57d552001-06-28 07:25:16 +00009864 if (*argptr)
9865 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009866 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9867 isloginsh = 1;
9868 /* bash does not accept +-login, we also won't */
9869 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009870 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009871 isloginsh = 1;
9872 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009873 } else {
9874 setoption(c, val);
9875 }
9876 }
9877 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009878 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009879}
9880
Eric Andersencb57d552001-06-28 07:25:16 +00009881/*
Eric Andersencb57d552001-06-28 07:25:16 +00009882 * The shift builtin command.
9883 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009884static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009885shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009886{
9887 int n;
9888 char **ap1, **ap2;
9889
9890 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009891 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009892 n = number(argv[1]);
9893 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009894 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009895 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009896 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009897 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009898 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009899 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009900 }
9901 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009902 while ((*ap2++ = *ap1++) != NULL)
9903 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009904#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009905 shellparam.optind = 1;
9906 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009907#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009908 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 return 0;
9910}
9911
Eric Andersencb57d552001-06-28 07:25:16 +00009912/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009913 * POSIX requires that 'set' (but not export or readonly) output the
9914 * variables in lexicographic order - by the locale's collating order (sigh).
9915 * Maybe we could keep them in an ordered balanced binary tree
9916 * instead of hashed lists.
9917 * For now just roll 'em through qsort for printing...
9918 */
9919static int
9920showvars(const char *sep_prefix, int on, int off)
9921{
9922 const char *sep;
9923 char **ep, **epend;
9924
9925 ep = listvars(on, off, &epend);
9926 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9927
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009928 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009929
9930 for (; ep < epend; ep++) {
9931 const char *p;
9932 const char *q;
9933
9934 p = strchrnul(*ep, '=');
9935 q = nullstr;
9936 if (*p)
9937 q = single_quote(++p);
9938 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9939 }
9940 return 0;
9941}
9942
9943/*
Eric Andersencb57d552001-06-28 07:25:16 +00009944 * The set command builtin.
9945 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009946static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009947setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009948{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009949 int retval;
9950
Denis Vlasenko68404f12008-03-17 09:00:54 +00009951 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009952 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009953 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009954 retval = 1;
9955 if (!options(0)) { /* if no parse error... */
9956 retval = 0;
9957 optschanged();
9958 if (*argptr != NULL) {
9959 setparam(argptr);
9960 }
Eric Andersencb57d552001-06-28 07:25:16 +00009961 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009962 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009963 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009964}
9965
Denis Vlasenko131ae172007-02-18 13:00:19 +00009966#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009967static void
9968change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009969{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009970 /* Galois LFSR parameter */
9971 /* Taps at 32 31 29 1: */
9972 enum { MASK = 0x8000000b };
9973 /* Another example - taps at 32 31 30 10: */
9974 /* MASK = 0x00400007 */
9975
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009976 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009977 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009978 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009979
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009980 /* LCG has period of 2^32 and alternating lowest bit */
9981 random_LCG = 1664525 * random_LCG + 1013904223;
9982 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9983 t = (random_galois_LFSR << 1);
9984 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9985 t ^= MASK;
9986 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009987 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009988 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009989 * for $RANDOM range. Combining with subtraction is
9990 * just for fun. + and ^ would work equally well. */
9991 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009992 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009993 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009994 vrandom.flags &= ~VNOFUNC;
9995 } else {
9996 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009997 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +00009998 }
Eric Andersenef02f822004-03-11 13:34:24 +00009999}
Eric Andersen16767e22004-03-16 05:14:10 +000010000#endif
10001
Denis Vlasenko131ae172007-02-18 13:00:19 +000010002#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010003static int
Eric Andersenc470f442003-07-28 09:56:35 +000010004getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010005{
10006 char *p, *q;
10007 char c = '?';
10008 int done = 0;
10009 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010010 char s[12];
10011 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010012
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010013 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010014 return 1;
10015 optnext = optfirst + *param_optind - 1;
10016
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010017 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010018 p = NULL;
10019 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010020 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010021 if (p == NULL || *p == '\0') {
10022 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010023 p = *optnext;
10024 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010025 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010026 p = NULL;
10027 done = 1;
10028 goto out;
10029 }
10030 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010031 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010032 goto atend;
10033 }
10034
10035 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010036 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010037 if (*q == '\0') {
10038 if (optstr[0] == ':') {
10039 s[0] = c;
10040 s[1] = '\0';
10041 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010042 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010043 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010044 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010045 }
10046 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010047 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
10049 if (*++q == ':')
10050 q++;
10051 }
10052
10053 if (*++q == ':') {
10054 if (*p == '\0' && (p = *optnext) == NULL) {
10055 if (optstr[0] == ':') {
10056 s[0] = c;
10057 s[1] = '\0';
10058 err |= setvarsafe("OPTARG", s, 0);
10059 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010060 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010061 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010062 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010063 c = '?';
10064 }
Eric Andersenc470f442003-07-28 09:56:35 +000010065 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010066 }
10067
10068 if (p == *optnext)
10069 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010070 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010071 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010072 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010073 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010074 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010075 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010076 *param_optind = optnext - optfirst + 1;
10077 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010078 err |= setvarsafe("OPTIND", s, VNOFUNC);
10079 s[0] = c;
10080 s[1] = '\0';
10081 err |= setvarsafe(optvar, s, 0);
10082 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010083 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010084 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010085 flush_stdout_stderr();
10086 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010087 }
10088 return done;
10089}
Eric Andersenc470f442003-07-28 09:56:35 +000010090
10091/*
10092 * The getopts builtin. Shellparam.optnext points to the next argument
10093 * to be processed. Shellparam.optptr points to the next character to
10094 * be processed in the current argument. If shellparam.optnext is NULL,
10095 * then it's the first time getopts has been called.
10096 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010097static int
Eric Andersenc470f442003-07-28 09:56:35 +000010098getoptscmd(int argc, char **argv)
10099{
10100 char **optbase;
10101
10102 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010103 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010104 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010105 optbase = shellparam.p;
10106 if (shellparam.optind > shellparam.nparam + 1) {
10107 shellparam.optind = 1;
10108 shellparam.optoff = -1;
10109 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010110 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010111 optbase = &argv[3];
10112 if (shellparam.optind > argc - 2) {
10113 shellparam.optind = 1;
10114 shellparam.optoff = -1;
10115 }
10116 }
10117
10118 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010119 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010120}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010121#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010122
Eric Andersencb57d552001-06-28 07:25:16 +000010123
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010124/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010125
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010126struct heredoc {
10127 struct heredoc *next; /* next here document in list */
10128 union node *here; /* redirection node */
10129 char *eofmark; /* string indicating end of input */
10130 smallint striptabs; /* if set, strip leading tabs */
10131};
10132
10133static smallint tokpushback; /* last token pushed back */
10134static smallint parsebackquote; /* nonzero if we are inside backquotes */
10135static smallint quoteflag; /* set if (part of) last token was quoted */
10136static token_id_t lasttoken; /* last token read (integer id Txxx) */
10137static struct heredoc *heredoclist; /* list of here documents to read */
10138static char *wordtext; /* text of last word returned by readtoken */
10139static struct nodelist *backquotelist;
10140static union node *redirnode;
10141static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010142/*
10143 * NEOF is returned by parsecmd when it encounters an end of file. It
10144 * must be distinct from NULL, so we use the address of a variable that
10145 * happens to be handy.
10146 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010147#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010148
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010149static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010150static void
10151raise_error_syntax(const char *msg)
10152{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010153 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010154 /* NOTREACHED */
10155}
10156
10157/*
10158 * Called when an unexpected token is read during the parse. The argument
10159 * is the token that is expected, or -1 if more than one type of token can
10160 * occur at this point.
10161 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010162static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010163static void
10164raise_error_unexpected_syntax(int token)
10165{
10166 char msg[64];
10167 int l;
10168
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010169 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010170 if (token >= 0)
10171 sprintf(msg + l, " (expecting %s)", tokname(token));
10172 raise_error_syntax(msg);
10173 /* NOTREACHED */
10174}
Eric Andersencb57d552001-06-28 07:25:16 +000010175
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010176#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010177
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010178/* parsing is heavily cross-recursive, need these forward decls */
10179static union node *andor(void);
10180static union node *pipeline(void);
10181static union node *parse_command(void);
10182static void parseheredoc(void);
10183static char peektoken(void);
10184static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010185
Eric Andersenc470f442003-07-28 09:56:35 +000010186static union node *
10187list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010188{
10189 union node *n1, *n2, *n3;
10190 int tok;
10191
Eric Andersenc470f442003-07-28 09:56:35 +000010192 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10193 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010194 return NULL;
10195 n1 = NULL;
10196 for (;;) {
10197 n2 = andor();
10198 tok = readtoken();
10199 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010200 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010201 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010202 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010203 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010204 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010205 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010206 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010207 n2 = n3;
10208 }
10209 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010210 }
10211 }
10212 if (n1 == NULL) {
10213 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010214 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010215 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010216 n3->type = NSEMI;
10217 n3->nbinary.ch1 = n1;
10218 n3->nbinary.ch2 = n2;
10219 n1 = n3;
10220 }
10221 switch (tok) {
10222 case TBACKGND:
10223 case TSEMI:
10224 tok = readtoken();
10225 /* fall through */
10226 case TNL:
10227 if (tok == TNL) {
10228 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010229 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010230 return n1;
10231 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010232 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010233 }
Eric Andersenc470f442003-07-28 09:56:35 +000010234 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010235 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010236 return n1;
10237 break;
10238 case TEOF:
10239 if (heredoclist)
10240 parseheredoc();
10241 else
Eric Andersenc470f442003-07-28 09:56:35 +000010242 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010243 return n1;
10244 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010245 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010246 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010247 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010248 return n1;
10249 }
10250 }
10251}
10252
Eric Andersenc470f442003-07-28 09:56:35 +000010253static union node *
10254andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010255{
Eric Andersencb57d552001-06-28 07:25:16 +000010256 union node *n1, *n2, *n3;
10257 int t;
10258
Eric Andersencb57d552001-06-28 07:25:16 +000010259 n1 = pipeline();
10260 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010261 t = readtoken();
10262 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010263 t = NAND;
10264 } else if (t == TOR) {
10265 t = NOR;
10266 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010267 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010268 return n1;
10269 }
Eric Andersenc470f442003-07-28 09:56:35 +000010270 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010271 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010272 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010273 n3->type = t;
10274 n3->nbinary.ch1 = n1;
10275 n3->nbinary.ch2 = n2;
10276 n1 = n3;
10277 }
10278}
10279
Eric Andersenc470f442003-07-28 09:56:35 +000010280static union node *
10281pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010282{
Eric Andersencb57d552001-06-28 07:25:16 +000010283 union node *n1, *n2, *pipenode;
10284 struct nodelist *lp, *prev;
10285 int negate;
10286
10287 negate = 0;
10288 TRACE(("pipeline: entered\n"));
10289 if (readtoken() == TNOT) {
10290 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010291 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010292 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010293 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010294 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010295 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010296 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010297 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010298 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010299 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010300 pipenode->npipe.cmdlist = lp;
10301 lp->n = n1;
10302 do {
10303 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010304 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010305 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010306 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010307 prev->next = lp;
10308 } while (readtoken() == TPIPE);
10309 lp->next = NULL;
10310 n1 = pipenode;
10311 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010312 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010313 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010314 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010315 n2->type = NNOT;
10316 n2->nnot.com = n1;
10317 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010318 }
10319 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010320}
10321
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010322static union node *
10323makename(void)
10324{
10325 union node *n;
10326
Denis Vlasenko597906c2008-02-20 16:38:54 +000010327 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010328 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010329 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010330 n->narg.text = wordtext;
10331 n->narg.backquote = backquotelist;
10332 return n;
10333}
10334
10335static void
10336fixredir(union node *n, const char *text, int err)
10337{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010338 int fd;
10339
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010340 TRACE(("Fix redir %s %d\n", text, err));
10341 if (!err)
10342 n->ndup.vname = NULL;
10343
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010344 fd = bb_strtou(text, NULL, 10);
10345 if (!errno && fd >= 0)
10346 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010347 else if (LONE_DASH(text))
10348 n->ndup.dupfd = -1;
10349 else {
10350 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010351 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010352 n->ndup.vname = makename();
10353 }
10354}
10355
10356/*
10357 * Returns true if the text contains nothing to expand (no dollar signs
10358 * or backquotes).
10359 */
10360static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010361noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010362{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010363 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010364 char c;
10365
10366 p = text;
10367 while ((c = *p++) != '\0') {
10368 if (c == CTLQUOTEMARK)
10369 continue;
10370 if (c == CTLESC)
10371 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010372 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010373 return 0;
10374 }
10375 return 1;
10376}
10377
10378static void
10379parsefname(void)
10380{
10381 union node *n = redirnode;
10382
10383 if (readtoken() != TWORD)
10384 raise_error_unexpected_syntax(-1);
10385 if (n->type == NHERE) {
10386 struct heredoc *here = heredoc;
10387 struct heredoc *p;
10388 int i;
10389
10390 if (quoteflag == 0)
10391 n->type = NXHERE;
10392 TRACE(("Here document %d\n", n->type));
10393 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010394 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010395 rmescapes(wordtext);
10396 here->eofmark = wordtext;
10397 here->next = NULL;
10398 if (heredoclist == NULL)
10399 heredoclist = here;
10400 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010401 for (p = heredoclist; p->next; p = p->next)
10402 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010403 p->next = here;
10404 }
10405 } else if (n->type == NTOFD || n->type == NFROMFD) {
10406 fixredir(n, wordtext, 0);
10407 } else {
10408 n->nfile.fname = makename();
10409 }
10410}
Eric Andersencb57d552001-06-28 07:25:16 +000010411
Eric Andersenc470f442003-07-28 09:56:35 +000010412static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010413simplecmd(void)
10414{
10415 union node *args, **app;
10416 union node *n = NULL;
10417 union node *vars, **vpp;
10418 union node **rpp, *redir;
10419 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010420#if ENABLE_ASH_BASH_COMPAT
10421 smallint double_brackets_flag = 0;
10422#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010423
10424 args = NULL;
10425 app = &args;
10426 vars = NULL;
10427 vpp = &vars;
10428 redir = NULL;
10429 rpp = &redir;
10430
10431 savecheckkwd = CHKALIAS;
10432 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010433 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010434 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010435 t = readtoken();
10436 switch (t) {
10437#if ENABLE_ASH_BASH_COMPAT
10438 case TAND: /* "&&" */
10439 case TOR: /* "||" */
10440 if (!double_brackets_flag) {
10441 tokpushback = 1;
10442 goto out;
10443 }
10444 wordtext = (char *) (t == TAND ? "-a" : "-o");
10445#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010446 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010447 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010448 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010449 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010450 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010451#if ENABLE_ASH_BASH_COMPAT
10452 if (strcmp("[[", wordtext) == 0)
10453 double_brackets_flag = 1;
10454 else if (strcmp("]]", wordtext) == 0)
10455 double_brackets_flag = 0;
10456#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010457 n->narg.backquote = backquotelist;
10458 if (savecheckkwd && isassignment(wordtext)) {
10459 *vpp = n;
10460 vpp = &n->narg.next;
10461 } else {
10462 *app = n;
10463 app = &n->narg.next;
10464 savecheckkwd = 0;
10465 }
10466 break;
10467 case TREDIR:
10468 *rpp = n = redirnode;
10469 rpp = &n->nfile.next;
10470 parsefname(); /* read name of redirection file */
10471 break;
10472 case TLP:
10473 if (args && app == &args->narg.next
10474 && !vars && !redir
10475 ) {
10476 struct builtincmd *bcmd;
10477 const char *name;
10478
10479 /* We have a function */
10480 if (readtoken() != TRP)
10481 raise_error_unexpected_syntax(TRP);
10482 name = n->narg.text;
10483 if (!goodname(name)
10484 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10485 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010486 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010487 }
10488 n->type = NDEFUN;
10489 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10490 n->narg.next = parse_command();
10491 return n;
10492 }
10493 /* fall through */
10494 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010495 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010496 goto out;
10497 }
10498 }
10499 out:
10500 *app = NULL;
10501 *vpp = NULL;
10502 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010503 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010504 n->type = NCMD;
10505 n->ncmd.args = args;
10506 n->ncmd.assign = vars;
10507 n->ncmd.redirect = redir;
10508 return n;
10509}
10510
10511static union node *
10512parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010513{
Eric Andersencb57d552001-06-28 07:25:16 +000010514 union node *n1, *n2;
10515 union node *ap, **app;
10516 union node *cp, **cpp;
10517 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010518 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010519 int t;
10520
10521 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010522 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010523
Eric Andersencb57d552001-06-28 07:25:16 +000010524 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010525 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010526 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010527 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010528 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010529 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010530 n1->type = NIF;
10531 n1->nif.test = list(0);
10532 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010533 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010534 n1->nif.ifpart = list(0);
10535 n2 = n1;
10536 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010537 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010538 n2 = n2->nif.elsepart;
10539 n2->type = NIF;
10540 n2->nif.test = list(0);
10541 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010542 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010543 n2->nif.ifpart = list(0);
10544 }
10545 if (lasttoken == TELSE)
10546 n2->nif.elsepart = list(0);
10547 else {
10548 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010549 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010550 }
Eric Andersenc470f442003-07-28 09:56:35 +000010551 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010552 break;
10553 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010554 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010555 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010556 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010557 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010558 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010559 got = readtoken();
10560 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010561 TRACE(("expecting DO got %s %s\n", tokname(got),
10562 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010563 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010564 }
10565 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010566 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010567 break;
10568 }
10569 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010570 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010571 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010572 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010573 n1->type = NFOR;
10574 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010575 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010576 if (readtoken() == TIN) {
10577 app = &ap;
10578 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010579 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010580 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010581 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010582 n2->narg.text = wordtext;
10583 n2->narg.backquote = backquotelist;
10584 *app = n2;
10585 app = &n2->narg.next;
10586 }
10587 *app = NULL;
10588 n1->nfor.args = ap;
10589 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010590 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010591 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010592 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010593 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010594 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010595 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010596 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010597 n1->nfor.args = n2;
10598 /*
10599 * Newline or semicolon here is optional (but note
10600 * that the original Bourne shell only allowed NL).
10601 */
10602 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010603 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010604 }
Eric Andersenc470f442003-07-28 09:56:35 +000010605 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010606 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010607 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010608 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010609 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010610 break;
10611 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010612 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010613 n1->type = NCASE;
10614 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010615 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010616 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010617 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010618 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010619 n2->narg.text = wordtext;
10620 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010621 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010622 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010623 } while (readtoken() == TNL);
10624 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010625 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010626 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010627 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010628 checkkwd = CHKNL | CHKKWD;
10629 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010630 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010631 if (lasttoken == TLP)
10632 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010633 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010634 cp->type = NCLIST;
10635 app = &cp->nclist.pattern;
10636 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010637 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010638 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010639 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010640 ap->narg.text = wordtext;
10641 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010642 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010643 break;
10644 app = &ap->narg.next;
10645 readtoken();
10646 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010647 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010648 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010649 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010650 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010651
Eric Andersenc470f442003-07-28 09:56:35 +000010652 cpp = &cp->nclist.next;
10653
10654 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010655 t = readtoken();
10656 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010657 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010658 raise_error_unexpected_syntax(TENDCASE);
10659 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010660 }
Eric Andersenc470f442003-07-28 09:56:35 +000010661 }
Eric Andersencb57d552001-06-28 07:25:16 +000010662 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010663 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010664 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010665 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010666 n1->type = NSUBSHELL;
10667 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010668 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010669 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010670 break;
10671 case TBEGIN:
10672 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010673 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010674 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010675 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010676 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010677 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010678 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010679 }
10680
Eric Andersenc470f442003-07-28 09:56:35 +000010681 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010682 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010683
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010684 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010685 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010686 checkkwd = CHKKWD | CHKALIAS;
10687 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010688 while (readtoken() == TREDIR) {
10689 *rpp = n2 = redirnode;
10690 rpp = &n2->nfile.next;
10691 parsefname();
10692 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010693 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010694 *rpp = NULL;
10695 if (redir) {
10696 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010697 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010698 n2->type = NREDIR;
10699 n2->nredir.n = n1;
10700 n1 = n2;
10701 }
10702 n1->nredir.redirect = redir;
10703 }
Eric Andersencb57d552001-06-28 07:25:16 +000010704 return n1;
10705}
10706
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010707#if ENABLE_ASH_BASH_COMPAT
10708static int decode_dollar_squote(void)
10709{
10710 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10711 int c, cnt;
10712 char *p;
10713 char buf[4];
10714
10715 c = pgetc();
10716 p = strchr(C_escapes, c);
10717 if (p) {
10718 buf[0] = c;
10719 p = buf;
10720 cnt = 3;
10721 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10722 do {
10723 c = pgetc();
10724 *++p = c;
10725 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10726 pungetc();
10727 } else if (c == 'x') { /* \xHH */
10728 do {
10729 c = pgetc();
10730 *++p = c;
10731 } while (isxdigit(c) && --cnt);
10732 pungetc();
10733 if (cnt == 3) { /* \x but next char is "bad" */
10734 c = 'x';
10735 goto unrecognized;
10736 }
10737 } else { /* simple seq like \\ or \t */
10738 p++;
10739 }
10740 *p = '\0';
10741 p = buf;
10742 c = bb_process_escape_sequence((void*)&p);
10743 } else { /* unrecognized "\z": print both chars unless ' or " */
10744 if (c != '\'' && c != '"') {
10745 unrecognized:
10746 c |= 0x100; /* "please encode \, then me" */
10747 }
10748 }
10749 return c;
10750}
10751#endif
10752
Eric Andersencb57d552001-06-28 07:25:16 +000010753/*
10754 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10755 * is not NULL, read a here document. In the latter case, eofmark is the
10756 * word which marks the end of the document and striptabs is true if
10757 * leading tabs should be stripped from the document. The argument firstc
10758 * is the first character of the input token or document.
10759 *
10760 * Because C does not have internal subroutines, I have simulated them
10761 * using goto's to implement the subroutine linkage. The following macros
10762 * will run code that appears at the end of readtoken1.
10763 */
Eric Andersen2870d962001-07-02 17:27:21 +000010764#define CHECKEND() {goto checkend; checkend_return:;}
10765#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10766#define PARSESUB() {goto parsesub; parsesub_return:;}
10767#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10768#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10769#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010770static int
Eric Andersenc470f442003-07-28 09:56:35 +000010771readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010772{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010773 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010774 int c = firstc;
10775 char *out;
10776 int len;
10777 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010778 struct nodelist *bqlist;
10779 smallint quotef;
10780 smallint dblquote;
10781 smallint oldstyle;
10782 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010783#if ENABLE_ASH_EXPAND_PRMT
10784 smallint pssyntax; /* we are expanding a prompt string */
10785#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010786 int varnest; /* levels of variables expansion */
10787 int arinest; /* levels of arithmetic expansion */
10788 int parenlevel; /* levels of parens in arithmetic */
10789 int dqvarnest; /* levels of variables expansion within double quotes */
10790
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010791 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10792
Eric Andersencb57d552001-06-28 07:25:16 +000010793#if __GNUC__
10794 /* Avoid longjmp clobbering */
10795 (void) &out;
10796 (void) &quotef;
10797 (void) &dblquote;
10798 (void) &varnest;
10799 (void) &arinest;
10800 (void) &parenlevel;
10801 (void) &dqvarnest;
10802 (void) &oldstyle;
10803 (void) &prevsyntax;
10804 (void) &syntax;
10805#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010806 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010807 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010808 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010809 oldstyle = 0;
10810 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010811#if ENABLE_ASH_EXPAND_PRMT
10812 pssyntax = (syntax == PSSYNTAX);
10813 if (pssyntax)
10814 syntax = DQSYNTAX;
10815#endif
10816 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010817 varnest = 0;
10818 arinest = 0;
10819 parenlevel = 0;
10820 dqvarnest = 0;
10821
10822 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010823 loop:
10824 /* For each line, until end of word */
10825 {
Eric Andersenc470f442003-07-28 09:56:35 +000010826 CHECKEND(); /* set c to PEOF if at end of here document */
10827 for (;;) { /* until end of line or end of word */
10828 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010829 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010830 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010831 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010832 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010833 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010834 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010835 if (doprompt)
10836 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010837 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010838 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010839 case CWORD:
10840 USTPUTC(c, out);
10841 break;
10842 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010843 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010844 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010845#if ENABLE_ASH_BASH_COMPAT
10846 if (c == '\\' && bash_dollar_squote) {
10847 c = decode_dollar_squote();
10848 if (c & 0x100) {
10849 USTPUTC('\\', out);
10850 c = (unsigned char)c;
10851 }
10852 }
10853#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010854 USTPUTC(c, out);
10855 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010856 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010857 c = pgetc2();
10858 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010859 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010860 USTPUTC('\\', out);
10861 pungetc();
10862 } else if (c == '\n') {
10863 if (doprompt)
10864 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010865 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010866#if ENABLE_ASH_EXPAND_PRMT
10867 if (c == '$' && pssyntax) {
10868 USTPUTC(CTLESC, out);
10869 USTPUTC('\\', out);
10870 }
10871#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010872 if (dblquote && c != '\\'
10873 && c != '`' && c != '$'
10874 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010875 ) {
10876 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010877 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010878 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010879 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010880 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010881 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010882 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010883 }
10884 break;
10885 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010886 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010887 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010888 if (eofmark == NULL) {
10889 USTPUTC(CTLQUOTEMARK, out);
10890 }
Eric Andersencb57d552001-06-28 07:25:16 +000010891 break;
10892 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010893 syntax = DQSYNTAX;
10894 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010895 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010896 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010897 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010898 if (eofmark != NULL && arinest == 0
10899 && varnest == 0
10900 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010901 USTPUTC(c, out);
10902 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010903 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010904 syntax = BASESYNTAX;
10905 dblquote = 0;
10906 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010907 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010908 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010909 }
10910 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010911 case CVAR: /* '$' */
10912 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010913 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010914 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010915 if (varnest > 0) {
10916 varnest--;
10917 if (dqvarnest > 0) {
10918 dqvarnest--;
10919 }
10920 USTPUTC(CTLENDVAR, out);
10921 } else {
10922 USTPUTC(c, out);
10923 }
10924 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010925#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010926 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010927 parenlevel++;
10928 USTPUTC(c, out);
10929 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010930 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010931 if (parenlevel > 0) {
10932 USTPUTC(c, out);
10933 --parenlevel;
10934 } else {
10935 if (pgetc() == ')') {
10936 if (--arinest == 0) {
10937 USTPUTC(CTLENDARI, out);
10938 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010939 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010940 } else
10941 USTPUTC(')', out);
10942 } else {
10943 /*
10944 * unbalanced parens
10945 * (don't 2nd guess - no error)
10946 */
10947 pungetc();
10948 USTPUTC(')', out);
10949 }
10950 }
10951 break;
10952#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010953 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010954 PARSEBACKQOLD();
10955 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010956 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010957 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010958 case CIGN:
10959 break;
10960 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010961 if (varnest == 0) {
10962#if ENABLE_ASH_BASH_COMPAT
10963 if (c == '&') {
10964 if (pgetc() == '>')
10965 c = 0x100 + '>'; /* flag &> */
10966 pungetc();
10967 }
10968#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010969 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010970 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010971#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010972 if (c != PEOA)
10973#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010974 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010975
Eric Andersencb57d552001-06-28 07:25:16 +000010976 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010977 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010978 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010979 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010980 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010981#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010982 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010983 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010984#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010985 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010986 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010987 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010988 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010989 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010990 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010991 }
10992 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010993 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010994 out = stackblock();
10995 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010996 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
10997 && quotef == 0
10998 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010999 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011000 PARSEREDIR(); /* passed as params: out, c */
11001 lasttoken = TREDIR;
11002 return lasttoken;
11003 }
11004 /* else: non-number X seen, interpret it
11005 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011006 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011007 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011008 }
11009 quoteflag = quotef;
11010 backquotelist = bqlist;
11011 grabstackblock(len);
11012 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011013 lasttoken = TWORD;
11014 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011015/* end of readtoken routine */
11016
Eric Andersencb57d552001-06-28 07:25:16 +000011017/*
11018 * Check to see whether we are at the end of the here document. When this
11019 * is called, c is set to the first character of the next input line. If
11020 * we are at the end of the here document, this routine sets the c to PEOF.
11021 */
Eric Andersenc470f442003-07-28 09:56:35 +000011022checkend: {
11023 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011024#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011025 if (c == PEOA) {
11026 c = pgetc2();
11027 }
11028#endif
11029 if (striptabs) {
11030 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000011031 c = pgetc2();
11032 }
Eric Andersenc470f442003-07-28 09:56:35 +000011033 }
11034 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011035 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011036 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011037
Eric Andersenc470f442003-07-28 09:56:35 +000011038 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011039 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11040 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011041 if (*p == '\n' && *q == '\0') {
11042 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011043 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011044 needprompt = doprompt;
11045 } else {
11046 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011047 }
11048 }
11049 }
11050 }
Eric Andersenc470f442003-07-28 09:56:35 +000011051 goto checkend_return;
11052}
Eric Andersencb57d552001-06-28 07:25:16 +000011053
Eric Andersencb57d552001-06-28 07:25:16 +000011054/*
11055 * Parse a redirection operator. The variable "out" points to a string
11056 * specifying the fd to be redirected. The variable "c" contains the
11057 * first character of the redirection operator.
11058 */
Eric Andersenc470f442003-07-28 09:56:35 +000011059parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011060 /* out is already checked to be a valid number or "" */
11061 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011062 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011063
Denis Vlasenko597906c2008-02-20 16:38:54 +000011064 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011065 if (c == '>') {
11066 np->nfile.fd = 1;
11067 c = pgetc();
11068 if (c == '>')
11069 np->type = NAPPEND;
11070 else if (c == '|')
11071 np->type = NCLOBBER;
11072 else if (c == '&')
11073 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011074 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011075 else {
11076 np->type = NTO;
11077 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011078 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011079 }
11080#if ENABLE_ASH_BASH_COMPAT
11081 else if (c == 0x100 + '>') { /* this flags &> redirection */
11082 np->nfile.fd = 1;
11083 pgetc(); /* this is '>', no need to check */
11084 np->type = NTO2;
11085 }
11086#endif
11087 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011088 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011089 c = pgetc();
11090 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011091 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011092 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011093 np = stzalloc(sizeof(struct nhere));
11094 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011095 }
11096 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011097 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011098 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011099 c = pgetc();
11100 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011101 heredoc->striptabs = 1;
11102 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011103 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011104 pungetc();
11105 }
11106 break;
11107
11108 case '&':
11109 np->type = NFROMFD;
11110 break;
11111
11112 case '>':
11113 np->type = NFROMTO;
11114 break;
11115
11116 default:
11117 np->type = NFROM;
11118 pungetc();
11119 break;
11120 }
Eric Andersencb57d552001-06-28 07:25:16 +000011121 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011122 if (fd >= 0)
11123 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011124 redirnode = np;
11125 goto parseredir_return;
11126}
Eric Andersencb57d552001-06-28 07:25:16 +000011127
Eric Andersencb57d552001-06-28 07:25:16 +000011128/*
11129 * Parse a substitution. At this point, we have read the dollar sign
11130 * and nothing else.
11131 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011132
11133/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11134 * (assuming ascii char codes, as the original implementation did) */
11135#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011136 (((unsigned)(c) - 33 < 32) \
11137 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011138parsesub: {
11139 int subtype;
11140 int typeloc;
11141 int flags;
11142 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011143 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011144
Eric Andersenc470f442003-07-28 09:56:35 +000011145 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011146 if (c <= PEOA_OR_PEOF
11147 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011148 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011149#if ENABLE_ASH_BASH_COMPAT
11150 if (c == '\'')
11151 bash_dollar_squote = 1;
11152 else
11153#endif
11154 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011155 pungetc();
11156 } else if (c == '(') { /* $(command) or $((arith)) */
11157 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011158#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011159 PARSEARITH();
11160#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011161 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011162#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011163 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011164 pungetc();
11165 PARSEBACKQNEW();
11166 }
11167 } else {
11168 USTPUTC(CTLVAR, out);
11169 typeloc = out - (char *)stackblock();
11170 USTPUTC(VSNORMAL, out);
11171 subtype = VSNORMAL;
11172 if (c == '{') {
11173 c = pgetc();
11174 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011175 c = pgetc();
11176 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011177 c = '#';
11178 else
11179 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011180 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011181 subtype = 0;
11182 }
11183 if (c > PEOA_OR_PEOF && is_name(c)) {
11184 do {
11185 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011186 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011187 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011188 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011189 do {
11190 STPUTC(c, out);
11191 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011192 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011193 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011194 USTPUTC(c, out);
11195 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011196 } else {
11197 badsub:
11198 raise_error_syntax("bad substitution");
11199 }
Eric Andersencb57d552001-06-28 07:25:16 +000011200
Eric Andersenc470f442003-07-28 09:56:35 +000011201 STPUTC('=', out);
11202 flags = 0;
11203 if (subtype == 0) {
11204 switch (c) {
11205 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011206 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011207#if ENABLE_ASH_BASH_COMPAT
11208 if (c == ':' || c == '$' || isdigit(c)) {
11209 pungetc();
11210 subtype = VSSUBSTR;
11211 break;
11212 }
11213#endif
11214 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011215 /*FALLTHROUGH*/
11216 default:
11217 p = strchr(types, c);
11218 if (p == NULL)
11219 goto badsub;
11220 subtype = p - types + VSNORMAL;
11221 break;
11222 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011223 case '#': {
11224 int cc = c;
11225 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11226 c = pgetc();
11227 if (c == cc)
11228 subtype++;
11229 else
11230 pungetc();
11231 break;
11232 }
11233#if ENABLE_ASH_BASH_COMPAT
11234 case '/':
11235 subtype = VSREPLACE;
11236 c = pgetc();
11237 if (c == '/')
11238 subtype++; /* VSREPLACEALL */
11239 else
11240 pungetc();
11241 break;
11242#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011243 }
Eric Andersenc470f442003-07-28 09:56:35 +000011244 } else {
11245 pungetc();
11246 }
11247 if (dblquote || arinest)
11248 flags |= VSQUOTE;
11249 *((char *)stackblock() + typeloc) = subtype | flags;
11250 if (subtype != VSNORMAL) {
11251 varnest++;
11252 if (dblquote || arinest) {
11253 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011254 }
11255 }
11256 }
Eric Andersenc470f442003-07-28 09:56:35 +000011257 goto parsesub_return;
11258}
Eric Andersencb57d552001-06-28 07:25:16 +000011259
Eric Andersencb57d552001-06-28 07:25:16 +000011260/*
11261 * Called to parse command substitutions. Newstyle is set if the command
11262 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11263 * list of commands (passed by reference), and savelen is the number of
11264 * characters on the top of the stack which must be preserved.
11265 */
Eric Andersenc470f442003-07-28 09:56:35 +000011266parsebackq: {
11267 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011268 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011269 union node *n;
11270 char *volatile str;
11271 struct jmploc jmploc;
11272 struct jmploc *volatile savehandler;
11273 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011274 smallint saveprompt = 0;
11275
Eric Andersencb57d552001-06-28 07:25:16 +000011276#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011277 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011278#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011279 savepbq = parsebackquote;
11280 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011281 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011282 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011283 exception_handler = savehandler;
11284 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011285 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011286 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011287 str = NULL;
11288 savelen = out - (char *)stackblock();
11289 if (savelen > 0) {
11290 str = ckmalloc(savelen);
11291 memcpy(str, stackblock(), savelen);
11292 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011293 savehandler = exception_handler;
11294 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011295 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011296 if (oldstyle) {
11297 /* We must read until the closing backquote, giving special
11298 treatment to some slashes, and then push the string and
11299 reread it as input, interpreting it normally. */
11300 char *pout;
11301 int pc;
11302 size_t psavelen;
11303 char *pstr;
11304
11305
11306 STARTSTACKSTR(pout);
11307 for (;;) {
11308 if (needprompt) {
11309 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011310 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011311 pc = pgetc();
11312 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011313 case '`':
11314 goto done;
11315
11316 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011317 pc = pgetc();
11318 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011319 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011320 if (doprompt)
11321 setprompt(2);
11322 /*
11323 * If eating a newline, avoid putting
11324 * the newline into the new character
11325 * stream (via the STPUTC after the
11326 * switch).
11327 */
11328 continue;
11329 }
11330 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011331 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011332 STPUTC('\\', pout);
11333 if (pc > PEOA_OR_PEOF) {
11334 break;
11335 }
11336 /* fall through */
11337
11338 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011339#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011340 case PEOA:
11341#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011342 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011343 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011344
11345 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011346 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011347 needprompt = doprompt;
11348 break;
11349
11350 default:
11351 break;
11352 }
11353 STPUTC(pc, pout);
11354 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011355 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011356 STPUTC('\0', pout);
11357 psavelen = pout - (char *)stackblock();
11358 if (psavelen > 0) {
11359 pstr = grabstackstr(pout);
11360 setinputstring(pstr);
11361 }
11362 }
11363 nlpp = &bqlist;
11364 while (*nlpp)
11365 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011366 *nlpp = stzalloc(sizeof(**nlpp));
11367 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011368 parsebackquote = oldstyle;
11369
11370 if (oldstyle) {
11371 saveprompt = doprompt;
11372 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011373 }
11374
Eric Andersenc470f442003-07-28 09:56:35 +000011375 n = list(2);
11376
11377 if (oldstyle)
11378 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011379 else if (readtoken() != TRP)
11380 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011381
11382 (*nlpp)->n = n;
11383 if (oldstyle) {
11384 /*
11385 * Start reading from old file again, ignoring any pushed back
11386 * tokens left from the backquote parsing
11387 */
11388 popfile();
11389 tokpushback = 0;
11390 }
11391 while (stackblocksize() <= savelen)
11392 growstackblock();
11393 STARTSTACKSTR(out);
11394 if (str) {
11395 memcpy(out, str, savelen);
11396 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011397 INT_OFF;
11398 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011399 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011400 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011401 }
11402 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011403 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011404 if (arinest || dblquote)
11405 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11406 else
11407 USTPUTC(CTLBACKQ, out);
11408 if (oldstyle)
11409 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011410 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011411}
11412
Denis Vlasenko131ae172007-02-18 13:00:19 +000011413#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011414/*
11415 * Parse an arithmetic expansion (indicate start of one and set state)
11416 */
Eric Andersenc470f442003-07-28 09:56:35 +000011417parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011418 if (++arinest == 1) {
11419 prevsyntax = syntax;
11420 syntax = ARISYNTAX;
11421 USTPUTC(CTLARI, out);
11422 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011423 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011424 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011425 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011426 } else {
11427 /*
11428 * we collapse embedded arithmetic expansion to
11429 * parenthesis, which should be equivalent
11430 */
11431 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011432 }
Eric Andersenc470f442003-07-28 09:56:35 +000011433 goto parsearith_return;
11434}
11435#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011436
Eric Andersenc470f442003-07-28 09:56:35 +000011437} /* end of readtoken */
11438
Eric Andersencb57d552001-06-28 07:25:16 +000011439/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011440 * Read the next input token.
11441 * If the token is a word, we set backquotelist to the list of cmds in
11442 * backquotes. We set quoteflag to true if any part of the word was
11443 * quoted.
11444 * If the token is TREDIR, then we set redirnode to a structure containing
11445 * the redirection.
11446 * In all cases, the variable startlinno is set to the number of the line
11447 * on which the token starts.
11448 *
11449 * [Change comment: here documents and internal procedures]
11450 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11451 * word parsing code into a separate routine. In this case, readtoken
11452 * doesn't need to have any internal procedures, but parseword does.
11453 * We could also make parseoperator in essence the main routine, and
11454 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011455 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011456#define NEW_xxreadtoken
11457#ifdef NEW_xxreadtoken
11458/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011459static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011460 '\n', '(', ')', /* singles */
11461 '&', '|', ';', /* doubles */
11462 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011463};
Eric Andersencb57d552001-06-28 07:25:16 +000011464
Denis Vlasenko834dee72008-10-07 09:18:30 +000011465#define xxreadtoken_singles 3
11466#define xxreadtoken_doubles 3
11467
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011468static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011469 TNL, TLP, TRP, /* only single occurrence allowed */
11470 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11471 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011472 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011473};
11474
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011475static int
11476xxreadtoken(void)
11477{
11478 int c;
11479
11480 if (tokpushback) {
11481 tokpushback = 0;
11482 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011483 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011484 if (needprompt) {
11485 setprompt(2);
11486 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011487 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011488 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011489 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011490 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11491 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011492
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011493 if (c == '#') {
11494 while ((c = pgetc()) != '\n' && c != PEOF)
11495 continue;
11496 pungetc();
11497 } else if (c == '\\') {
11498 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011499 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011500 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011501 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011502 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011503 if (doprompt)
11504 setprompt(2);
11505 } else {
11506 const char *p;
11507
11508 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11509 if (c != PEOF) {
11510 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011511 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011512 needprompt = doprompt;
11513 }
11514
11515 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011516 if (p == NULL)
11517 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011518
Denis Vlasenko834dee72008-10-07 09:18:30 +000011519 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11520 int cc = pgetc();
11521 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011522 p += xxreadtoken_doubles + 1;
11523 } else {
11524 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011525#if ENABLE_ASH_BASH_COMPAT
11526 if (c == '&' && cc == '>') /* &> */
11527 break; /* return readtoken1(...) */
11528#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011529 }
11530 }
11531 }
11532 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11533 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011534 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011535 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011536
11537 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011538}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011539#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011540#define RETURN(token) return lasttoken = token
11541static int
11542xxreadtoken(void)
11543{
11544 int c;
11545
11546 if (tokpushback) {
11547 tokpushback = 0;
11548 return lasttoken;
11549 }
11550 if (needprompt) {
11551 setprompt(2);
11552 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011553 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011554 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011555 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011556 switch (c) {
11557 case ' ': case '\t':
11558#if ENABLE_ASH_ALIAS
11559 case PEOA:
11560#endif
11561 continue;
11562 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011563 while ((c = pgetc()) != '\n' && c != PEOF)
11564 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011565 pungetc();
11566 continue;
11567 case '\\':
11568 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011569 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011570 if (doprompt)
11571 setprompt(2);
11572 continue;
11573 }
11574 pungetc();
11575 goto breakloop;
11576 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011577 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011578 needprompt = doprompt;
11579 RETURN(TNL);
11580 case PEOF:
11581 RETURN(TEOF);
11582 case '&':
11583 if (pgetc() == '&')
11584 RETURN(TAND);
11585 pungetc();
11586 RETURN(TBACKGND);
11587 case '|':
11588 if (pgetc() == '|')
11589 RETURN(TOR);
11590 pungetc();
11591 RETURN(TPIPE);
11592 case ';':
11593 if (pgetc() == ';')
11594 RETURN(TENDCASE);
11595 pungetc();
11596 RETURN(TSEMI);
11597 case '(':
11598 RETURN(TLP);
11599 case ')':
11600 RETURN(TRP);
11601 default:
11602 goto breakloop;
11603 }
11604 }
11605 breakloop:
11606 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11607#undef RETURN
11608}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011609#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011610
11611static int
11612readtoken(void)
11613{
11614 int t;
11615#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011616 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011617#endif
11618
11619#if ENABLE_ASH_ALIAS
11620 top:
11621#endif
11622
11623 t = xxreadtoken();
11624
11625 /*
11626 * eat newlines
11627 */
11628 if (checkkwd & CHKNL) {
11629 while (t == TNL) {
11630 parseheredoc();
11631 t = xxreadtoken();
11632 }
11633 }
11634
11635 if (t != TWORD || quoteflag) {
11636 goto out;
11637 }
11638
11639 /*
11640 * check for keywords
11641 */
11642 if (checkkwd & CHKKWD) {
11643 const char *const *pp;
11644
11645 pp = findkwd(wordtext);
11646 if (pp) {
11647 lasttoken = t = pp - tokname_array;
11648 TRACE(("keyword %s recognized\n", tokname(t)));
11649 goto out;
11650 }
11651 }
11652
11653 if (checkkwd & CHKALIAS) {
11654#if ENABLE_ASH_ALIAS
11655 struct alias *ap;
11656 ap = lookupalias(wordtext, 1);
11657 if (ap != NULL) {
11658 if (*ap->val) {
11659 pushstring(ap->val, ap);
11660 }
11661 goto top;
11662 }
11663#endif
11664 }
11665 out:
11666 checkkwd = 0;
11667#if DEBUG
11668 if (!alreadyseen)
11669 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11670 else
11671 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11672#endif
11673 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011674}
11675
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011676static char
11677peektoken(void)
11678{
11679 int t;
11680
11681 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011682 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011683 return tokname_array[t][0];
11684}
Eric Andersencb57d552001-06-28 07:25:16 +000011685
11686/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011687 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11688 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011689 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011690static union node *
11691parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011692{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011693 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011694
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011695 tokpushback = 0;
11696 doprompt = interact;
11697 if (doprompt)
11698 setprompt(doprompt);
11699 needprompt = 0;
11700 t = readtoken();
11701 if (t == TEOF)
11702 return NEOF;
11703 if (t == TNL)
11704 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011705 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011706 return list(1);
11707}
11708
11709/*
11710 * Input any here documents.
11711 */
11712static void
11713parseheredoc(void)
11714{
11715 struct heredoc *here;
11716 union node *n;
11717
11718 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011719 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011720
11721 while (here) {
11722 if (needprompt) {
11723 setprompt(2);
11724 }
11725 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11726 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011727 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011728 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011729 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011730 n->narg.text = wordtext;
11731 n->narg.backquote = backquotelist;
11732 here->here->nhere.doc = n;
11733 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011734 }
Eric Andersencb57d552001-06-28 07:25:16 +000011735}
11736
11737
11738/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011739 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011740 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011741#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011742static const char *
11743expandstr(const char *ps)
11744{
11745 union node n;
11746
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011747 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11748 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011749 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011750 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011751 popfile();
11752
11753 n.narg.type = NARG;
11754 n.narg.next = NULL;
11755 n.narg.text = wordtext;
11756 n.narg.backquote = backquotelist;
11757
11758 expandarg(&n, NULL, 0);
11759 return stackblock();
11760}
11761#endif
11762
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011763/*
11764 * Execute a command or commands contained in a string.
11765 */
11766static int
11767evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011768{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011769 union node *n;
11770 struct stackmark smark;
11771 int skip;
11772
11773 setinputstring(s);
11774 setstackmark(&smark);
11775
11776 skip = 0;
11777 while ((n = parsecmd(0)) != NEOF) {
11778 evaltree(n, 0);
11779 popstackmark(&smark);
11780 skip = evalskip;
11781 if (skip)
11782 break;
11783 }
11784 popfile();
11785
11786 skip &= mask;
11787 evalskip = skip;
11788 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011789}
11790
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011791/*
11792 * The eval command.
11793 */
11794static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011795evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011796{
11797 char *p;
11798 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011799
Denis Vlasenko68404f12008-03-17 09:00:54 +000011800 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011801 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011802 argv += 2;
11803 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011804 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011805 for (;;) {
11806 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011807 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011808 if (p == NULL)
11809 break;
11810 STPUTC(' ', concat);
11811 }
11812 STPUTC('\0', concat);
11813 p = grabstackstr(concat);
11814 }
11815 evalstring(p, ~SKIPEVAL);
11816
11817 }
11818 return exitstatus;
11819}
11820
11821/*
11822 * Read and execute commands. "Top" is nonzero for the top level command
11823 * loop; it turns on prompting if the shell is interactive.
11824 */
11825static int
11826cmdloop(int top)
11827{
11828 union node *n;
11829 struct stackmark smark;
11830 int inter;
11831 int numeof = 0;
11832
11833 TRACE(("cmdloop(%d) called\n", top));
11834 for (;;) {
11835 int skip;
11836
11837 setstackmark(&smark);
11838#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011839 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011840 showjobs(stderr, SHOW_CHANGED);
11841#endif
11842 inter = 0;
11843 if (iflag && top) {
11844 inter++;
11845#if ENABLE_ASH_MAIL
11846 chkmail();
11847#endif
11848 }
11849 n = parsecmd(inter);
11850 /* showtree(n); DEBUG */
11851 if (n == NEOF) {
11852 if (!top || numeof >= 50)
11853 break;
11854 if (!stoppedjobs()) {
11855 if (!Iflag)
11856 break;
11857 out2str("\nUse \"exit\" to leave shell.\n");
11858 }
11859 numeof++;
11860 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011861 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11862 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011863 numeof = 0;
11864 evaltree(n, 0);
11865 }
11866 popstackmark(&smark);
11867 skip = evalskip;
11868
11869 if (skip) {
11870 evalskip = 0;
11871 return skip & SKIPEVAL;
11872 }
11873 }
11874 return 0;
11875}
11876
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011877/*
11878 * Take commands from a file. To be compatible we should do a path
11879 * search for the file, which is necessary to find sub-commands.
11880 */
11881static char *
11882find_dot_file(char *name)
11883{
11884 char *fullname;
11885 const char *path = pathval();
11886 struct stat statb;
11887
11888 /* don't try this for absolute or relative paths */
11889 if (strchr(name, '/'))
11890 return name;
11891
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011892 /* IIRC standards do not say whether . is to be searched.
11893 * And it is even smaller this way, making it unconditional for now:
11894 */
11895 if (1) { /* ENABLE_ASH_BASH_COMPAT */
11896 fullname = name;
11897 goto try_cur_dir;
11898 }
11899
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011900 while ((fullname = padvance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011901 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011902 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11903 /*
11904 * Don't bother freeing here, since it will
11905 * be freed by the caller.
11906 */
11907 return fullname;
11908 }
11909 stunalloc(fullname);
11910 }
11911
11912 /* not found in the PATH */
11913 ash_msg_and_raise_error("%s: not found", name);
11914 /* NOTREACHED */
11915}
11916
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011917static int
11918dotcmd(int argc, char **argv)
11919{
11920 struct strlist *sp;
11921 volatile struct shparam saveparam;
11922 int status = 0;
11923
11924 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011925 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011926
Denis Vlasenko68404f12008-03-17 09:00:54 +000011927 if (argv[1]) { /* That's what SVR2 does */
11928 char *fullname = find_dot_file(argv[1]);
11929 argv += 2;
11930 argc -= 2;
11931 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011932 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011933 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011934 shellparam.nparam = argc;
11935 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011936 };
11937
11938 setinputfile(fullname, INPUT_PUSH_FILE);
11939 commandname = fullname;
11940 cmdloop(0);
11941 popfile();
11942
Denis Vlasenko68404f12008-03-17 09:00:54 +000011943 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011944 freeparam(&shellparam);
11945 shellparam = saveparam;
11946 };
11947 status = exitstatus;
11948 }
11949 return status;
11950}
11951
11952static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011953exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011954{
11955 if (stoppedjobs())
11956 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011957 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011958 exitstatus = number(argv[1]);
11959 raise_exception(EXEXIT);
11960 /* NOTREACHED */
11961}
11962
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011963/*
11964 * Read a file containing shell functions.
11965 */
11966static void
11967readcmdfile(char *name)
11968{
11969 setinputfile(name, INPUT_PUSH_FILE);
11970 cmdloop(0);
11971 popfile();
11972}
11973
11974
Denis Vlasenkocc571512007-02-23 21:10:35 +000011975/* ============ find_command inplementation */
11976
11977/*
11978 * Resolve a command name. If you change this routine, you may have to
11979 * change the shellexec routine as well.
11980 */
11981static void
11982find_command(char *name, struct cmdentry *entry, int act, const char *path)
11983{
11984 struct tblentry *cmdp;
11985 int idx;
11986 int prev;
11987 char *fullname;
11988 struct stat statb;
11989 int e;
11990 int updatetbl;
11991 struct builtincmd *bcmd;
11992
11993 /* If name contains a slash, don't use PATH or hash table */
11994 if (strchr(name, '/') != NULL) {
11995 entry->u.index = -1;
11996 if (act & DO_ABS) {
11997 while (stat(name, &statb) < 0) {
11998#ifdef SYSV
11999 if (errno == EINTR)
12000 continue;
12001#endif
12002 entry->cmdtype = CMDUNKNOWN;
12003 return;
12004 }
12005 }
12006 entry->cmdtype = CMDNORMAL;
12007 return;
12008 }
12009
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012010/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012011
12012 updatetbl = (path == pathval());
12013 if (!updatetbl) {
12014 act |= DO_ALTPATH;
12015 if (strstr(path, "%builtin") != NULL)
12016 act |= DO_ALTBLTIN;
12017 }
12018
12019 /* If name is in the table, check answer will be ok */
12020 cmdp = cmdlookup(name, 0);
12021 if (cmdp != NULL) {
12022 int bit;
12023
12024 switch (cmdp->cmdtype) {
12025 default:
12026#if DEBUG
12027 abort();
12028#endif
12029 case CMDNORMAL:
12030 bit = DO_ALTPATH;
12031 break;
12032 case CMDFUNCTION:
12033 bit = DO_NOFUNC;
12034 break;
12035 case CMDBUILTIN:
12036 bit = DO_ALTBLTIN;
12037 break;
12038 }
12039 if (act & bit) {
12040 updatetbl = 0;
12041 cmdp = NULL;
12042 } else if (cmdp->rehash == 0)
12043 /* if not invalidated by cd, we're done */
12044 goto success;
12045 }
12046
12047 /* If %builtin not in path, check for builtin next */
12048 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012049 if (bcmd) {
12050 if (IS_BUILTIN_REGULAR(bcmd))
12051 goto builtin_success;
12052 if (act & DO_ALTPATH) {
12053 if (!(act & DO_ALTBLTIN))
12054 goto builtin_success;
12055 } else if (builtinloc <= 0) {
12056 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012057 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012058 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012059
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012060#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012061 {
12062 int applet_no = find_applet_by_name(name);
12063 if (applet_no >= 0) {
12064 entry->cmdtype = CMDNORMAL;
12065 entry->u.index = -2 - applet_no;
12066 return;
12067 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012068 }
12069#endif
12070
Denis Vlasenkocc571512007-02-23 21:10:35 +000012071 /* We have to search path. */
12072 prev = -1; /* where to start */
12073 if (cmdp && cmdp->rehash) { /* doing a rehash */
12074 if (cmdp->cmdtype == CMDBUILTIN)
12075 prev = builtinloc;
12076 else
12077 prev = cmdp->param.index;
12078 }
12079
12080 e = ENOENT;
12081 idx = -1;
12082 loop:
12083 while ((fullname = padvance(&path, name)) != NULL) {
12084 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012085 /* NB: code below will still use fullname
12086 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012087 idx++;
12088 if (pathopt) {
12089 if (prefix(pathopt, "builtin")) {
12090 if (bcmd)
12091 goto builtin_success;
12092 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012093 }
12094 if ((act & DO_NOFUNC)
12095 || !prefix(pathopt, "func")
12096 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012097 continue;
12098 }
12099 }
12100 /* if rehash, don't redo absolute path names */
12101 if (fullname[0] == '/' && idx <= prev) {
12102 if (idx < prev)
12103 continue;
12104 TRACE(("searchexec \"%s\": no change\n", name));
12105 goto success;
12106 }
12107 while (stat(fullname, &statb) < 0) {
12108#ifdef SYSV
12109 if (errno == EINTR)
12110 continue;
12111#endif
12112 if (errno != ENOENT && errno != ENOTDIR)
12113 e = errno;
12114 goto loop;
12115 }
12116 e = EACCES; /* if we fail, this will be the error */
12117 if (!S_ISREG(statb.st_mode))
12118 continue;
12119 if (pathopt) { /* this is a %func directory */
12120 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012121 /* NB: stalloc will return space pointed by fullname
12122 * (because we don't have any intervening allocations
12123 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012124 readcmdfile(fullname);
12125 cmdp = cmdlookup(name, 0);
12126 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12127 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12128 stunalloc(fullname);
12129 goto success;
12130 }
12131 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12132 if (!updatetbl) {
12133 entry->cmdtype = CMDNORMAL;
12134 entry->u.index = idx;
12135 return;
12136 }
12137 INT_OFF;
12138 cmdp = cmdlookup(name, 1);
12139 cmdp->cmdtype = CMDNORMAL;
12140 cmdp->param.index = idx;
12141 INT_ON;
12142 goto success;
12143 }
12144
12145 /* We failed. If there was an entry for this command, delete it */
12146 if (cmdp && updatetbl)
12147 delete_cmd_entry();
12148 if (act & DO_ERR)
12149 ash_msg("%s: %s", name, errmsg(e, "not found"));
12150 entry->cmdtype = CMDUNKNOWN;
12151 return;
12152
12153 builtin_success:
12154 if (!updatetbl) {
12155 entry->cmdtype = CMDBUILTIN;
12156 entry->u.cmd = bcmd;
12157 return;
12158 }
12159 INT_OFF;
12160 cmdp = cmdlookup(name, 1);
12161 cmdp->cmdtype = CMDBUILTIN;
12162 cmdp->param.cmd = bcmd;
12163 INT_ON;
12164 success:
12165 cmdp->rehash = 0;
12166 entry->cmdtype = cmdp->cmdtype;
12167 entry->u = cmdp->param;
12168}
12169
12170
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012171/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012172
Eric Andersencb57d552001-06-28 07:25:16 +000012173/*
Eric Andersencb57d552001-06-28 07:25:16 +000012174 * The trap builtin.
12175 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012176static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012177trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012178{
12179 char *action;
12180 char **ap;
12181 int signo;
12182
Eric Andersenc470f442003-07-28 09:56:35 +000012183 nextopt(nullstr);
12184 ap = argptr;
12185 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012186 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012187 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012188 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012189 single_quote(trap[signo]),
12190 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012191 }
12192 }
12193 return 0;
12194 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012195 action = NULL;
12196 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012197 action = *ap++;
12198 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012199 signo = get_signum(*ap);
12200 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012201 ash_msg_and_raise_error("%s: bad trap", *ap);
12202 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012203 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012204 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012205 action = NULL;
12206 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012207 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012208 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012209 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012210 trap[signo] = action;
12211 if (signo != 0)
12212 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012213 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012214 ap++;
12215 }
12216 return 0;
12217}
12218
Eric Andersenc470f442003-07-28 09:56:35 +000012219
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012220/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012221
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012222#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012223/*
12224 * Lists available builtins
12225 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012226static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012227helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012228{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012229 unsigned col;
12230 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012231
12232 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012233 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012234 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012235 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012236 if (col > 60) {
12237 out1fmt("\n");
12238 col = 0;
12239 }
12240 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012241#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012242 {
12243 const char *a = applet_names;
12244 while (*a) {
12245 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12246 if (col > 60) {
12247 out1fmt("\n");
12248 col = 0;
12249 }
12250 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012251 }
12252 }
12253#endif
12254 out1fmt("\n\n");
12255 return EXIT_SUCCESS;
12256}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012257#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012258
Eric Andersencb57d552001-06-28 07:25:16 +000012259/*
Eric Andersencb57d552001-06-28 07:25:16 +000012260 * The export and readonly commands.
12261 */
Eric Andersenc470f442003-07-28 09:56:35 +000012262static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012263exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012264{
12265 struct var *vp;
12266 char *name;
12267 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012268 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012269 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012270
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012271 if (nextopt("p") != 'p') {
12272 aptr = argptr;
12273 name = *aptr;
12274 if (name) {
12275 do {
12276 p = strchr(name, '=');
12277 if (p != NULL) {
12278 p++;
12279 } else {
12280 vp = *findvar(hashvar(name), name);
12281 if (vp) {
12282 vp->flags |= flag;
12283 continue;
12284 }
Eric Andersencb57d552001-06-28 07:25:16 +000012285 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012286 setvar(name, p, flag);
12287 } while ((name = *++aptr) != NULL);
12288 return 0;
12289 }
Eric Andersencb57d552001-06-28 07:25:16 +000012290 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012291 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012292 return 0;
12293}
12294
Eric Andersencb57d552001-06-28 07:25:16 +000012295/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012296 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012297 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012298static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012299unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012300{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012301 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012302
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012303 cmdp = cmdlookup(name, 0);
12304 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12305 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012306}
12307
Eric Andersencb57d552001-06-28 07:25:16 +000012308/*
Eric Andersencb57d552001-06-28 07:25:16 +000012309 * The unset builtin command. We unset the function before we unset the
12310 * variable to allow a function to be unset when there is a readonly variable
12311 * with the same name.
12312 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012313static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012314unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012315{
12316 char **ap;
12317 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012318 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012319 int ret = 0;
12320
12321 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012322 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012323 }
Eric Andersencb57d552001-06-28 07:25:16 +000012324
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012325 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012326 if (flag != 'f') {
12327 i = unsetvar(*ap);
12328 ret |= i;
12329 if (!(i & 2))
12330 continue;
12331 }
12332 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012333 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012334 }
Eric Andersenc470f442003-07-28 09:56:35 +000012335 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012336}
12337
12338
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012339/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012340
Eric Andersenc470f442003-07-28 09:56:35 +000012341#include <sys/times.h>
12342
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012343static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012344 ' ', offsetof(struct tms, tms_utime),
12345 '\n', offsetof(struct tms, tms_stime),
12346 ' ', offsetof(struct tms, tms_cutime),
12347 '\n', offsetof(struct tms, tms_cstime),
12348 0
12349};
Eric Andersencb57d552001-06-28 07:25:16 +000012350
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012351static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012352timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012353{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012354 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012355 const unsigned char *p;
12356 struct tms buf;
12357
12358 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012359 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012360
12361 p = timescmd_str;
12362 do {
12363 t = *(clock_t *)(((char *) &buf) + p[1]);
12364 s = t / clk_tck;
12365 out1fmt("%ldm%ld.%.3lds%c",
12366 s/60, s%60,
12367 ((t - s * clk_tck) * 1000) / clk_tck,
12368 p[0]);
12369 } while (*(p += 2));
12370
Eric Andersencb57d552001-06-28 07:25:16 +000012371 return 0;
12372}
12373
Denis Vlasenko131ae172007-02-18 13:00:19 +000012374#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012375static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012376dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012377{
Eric Andersened9ecf72004-06-22 08:29:45 +000012378 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012379 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012380
Denis Vlasenkob012b102007-02-19 22:43:01 +000012381 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012382 result = arith(s, &errcode);
12383 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012384 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012385 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012386 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012387 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012388 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012389 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012390 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012391 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012392 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012393
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012394 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012395}
Eric Andersenc470f442003-07-28 09:56:35 +000012396
Eric Andersenc470f442003-07-28 09:56:35 +000012397/*
Eric Andersen90898442003-08-06 11:20:52 +000012398 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12399 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12400 *
12401 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012402 */
12403static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012404letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012405{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012406 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012407
Denis Vlasenko68404f12008-03-17 09:00:54 +000012408 argv++;
12409 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012410 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012411 do {
12412 i = dash_arith(*argv);
12413 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012414
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012415 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012416}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012417#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012418
Eric Andersenc470f442003-07-28 09:56:35 +000012419
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012420/* ============ miscbltin.c
12421 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012422 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012423 */
12424
12425#undef rflag
12426
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012427#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012428typedef enum __rlimit_resource rlim_t;
12429#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012430
Eric Andersenc470f442003-07-28 09:56:35 +000012431/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012432 * The read builtin. Options:
12433 * -r Do not interpret '\' specially
12434 * -s Turn off echo (tty only)
12435 * -n NCHARS Read NCHARS max
12436 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12437 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12438 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012439 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012440 * TODO: bash also has:
12441 * -a ARRAY Read into array[0],[1],etc
12442 * -d DELIM End on DELIM char, not newline
12443 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012444 */
Eric Andersenc470f442003-07-28 09:56:35 +000012445static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012446readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012447{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012448 static const char *const arg_REPLY[] = { "REPLY", NULL };
12449
Eric Andersenc470f442003-07-28 09:56:35 +000012450 char **ap;
12451 int backslash;
12452 char c;
12453 int rflag;
12454 char *prompt;
12455 const char *ifs;
12456 char *p;
12457 int startword;
12458 int status;
12459 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012460 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012461#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012462 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012463 int silent = 0;
12464 struct termios tty, old_tty;
12465#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012466#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012467 unsigned end_ms = 0;
12468 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012469#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012470
12471 rflag = 0;
12472 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012473 while ((i = nextopt("p:u:r"
12474 USE_ASH_READ_TIMEOUT("t:")
12475 USE_ASH_READ_NCHARS("n:s")
12476 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012477 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012478 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012479 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012480 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012481#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012482 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012483 nchars = bb_strtou(optionarg, NULL, 10);
12484 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012485 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012486 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012487 break;
12488 case 's':
12489 silent = 1;
12490 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012491#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012492#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012493 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012494 timeout = bb_strtou(optionarg, NULL, 10);
12495 if (errno || timeout > UINT_MAX / 2048)
12496 ash_msg_and_raise_error("invalid timeout");
12497 timeout *= 1000;
12498#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012499 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012500 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012501 /* EINVAL means number is ok, but not terminated by NUL */
12502 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012503 char *p2;
12504 if (*++p) {
12505 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012506 ts.tv_usec = bb_strtou(p, &p2, 10);
12507 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012508 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012509 scale = p2 - p;
12510 /* normalize to usec */
12511 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012512 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012513 while (scale++ < 6)
12514 ts.tv_usec *= 10;
12515 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012516 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012517 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012518 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012519 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012520 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012521 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012522#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012523 break;
12524#endif
12525 case 'r':
12526 rflag = 1;
12527 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012528 case 'u':
12529 fd = bb_strtou(optionarg, NULL, 10);
12530 if (fd < 0 || errno)
12531 ash_msg_and_raise_error("invalid file descriptor");
12532 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012533 default:
12534 break;
12535 }
Eric Andersenc470f442003-07-28 09:56:35 +000012536 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012537 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012538 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012539 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012540 ap = argptr;
12541 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012542 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012543 ifs = bltinlookup("IFS");
12544 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012545 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012546#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012547 tcgetattr(fd, &tty);
12548 old_tty = tty;
12549 if (nchars || silent) {
12550 if (nchars) {
12551 tty.c_lflag &= ~ICANON;
12552 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012553 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012554 if (silent) {
12555 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12556 }
12557 /* if tcgetattr failed, tcsetattr will fail too.
12558 * Ignoring, it's harmless. */
12559 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012560 }
12561#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012562
Eric Andersenc470f442003-07-28 09:56:35 +000012563 status = 0;
12564 startword = 1;
12565 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012566#if ENABLE_ASH_READ_TIMEOUT
12567 if (timeout) /* NB: ensuring end_ms is nonzero */
12568 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12569#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012570 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012571 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012572#if ENABLE_ASH_READ_TIMEOUT
12573 if (end_ms) {
12574 struct pollfd pfd[1];
12575 pfd[0].fd = fd;
12576 pfd[0].events = POLLIN;
12577 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12578 if ((int)timeout <= 0 /* already late? */
12579 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12580 ) { /* timed out! */
12581#if ENABLE_ASH_READ_NCHARS
12582 tcsetattr(fd, TCSANOW, &old_tty);
12583#endif
12584 return 1;
12585 }
12586 }
12587#endif
12588 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012589 status = 1;
12590 break;
12591 }
12592 if (c == '\0')
12593 continue;
12594 if (backslash) {
12595 backslash = 0;
12596 if (c != '\n')
12597 goto put;
12598 continue;
12599 }
12600 if (!rflag && c == '\\') {
12601 backslash++;
12602 continue;
12603 }
12604 if (c == '\n')
12605 break;
12606 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12607 continue;
12608 }
12609 startword = 0;
12610 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12611 STACKSTRNUL(p);
12612 setvar(*ap, stackblock(), 0);
12613 ap++;
12614 startword = 1;
12615 STARTSTACKSTR(p);
12616 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012617 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012618 STPUTC(c, p);
12619 }
12620 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012621/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012622#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012623 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012624#else
12625 while (1);
12626#endif
12627
12628#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012629 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012630#endif
12631
Eric Andersenc470f442003-07-28 09:56:35 +000012632 STACKSTRNUL(p);
12633 /* Remove trailing blanks */
12634 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12635 *p = '\0';
12636 setvar(*ap, stackblock(), 0);
12637 while (*++ap != NULL)
12638 setvar(*ap, nullstr, 0);
12639 return status;
12640}
12641
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012642static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012643umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012644{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012645 static const char permuser[3] ALIGN1 = "ugo";
12646 static const char permmode[3] ALIGN1 = "rwx";
12647 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012648 S_IRUSR, S_IWUSR, S_IXUSR,
12649 S_IRGRP, S_IWGRP, S_IXGRP,
12650 S_IROTH, S_IWOTH, S_IXOTH
12651 };
12652
12653 char *ap;
12654 mode_t mask;
12655 int i;
12656 int symbolic_mode = 0;
12657
12658 while (nextopt("S") != '\0') {
12659 symbolic_mode = 1;
12660 }
12661
Denis Vlasenkob012b102007-02-19 22:43:01 +000012662 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012663 mask = umask(0);
12664 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012665 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012666
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012667 ap = *argptr;
12668 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012669 if (symbolic_mode) {
12670 char buf[18];
12671 char *p = buf;
12672
12673 for (i = 0; i < 3; i++) {
12674 int j;
12675
12676 *p++ = permuser[i];
12677 *p++ = '=';
12678 for (j = 0; j < 3; j++) {
12679 if ((mask & permmask[3 * i + j]) == 0) {
12680 *p++ = permmode[j];
12681 }
12682 }
12683 *p++ = ',';
12684 }
12685 *--p = 0;
12686 puts(buf);
12687 } else {
12688 out1fmt("%.4o\n", mask);
12689 }
12690 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012691 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012692 mask = 0;
12693 do {
12694 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012695 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012696 mask = (mask << 3) + (*ap - '0');
12697 } while (*++ap != '\0');
12698 umask(mask);
12699 } else {
12700 mask = ~mask & 0777;
12701 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012702 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012703 }
12704 umask(~mask & 0777);
12705 }
12706 }
12707 return 0;
12708}
12709
12710/*
12711 * ulimit builtin
12712 *
12713 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12714 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12715 * ash by J.T. Conklin.
12716 *
12717 * Public domain.
12718 */
12719
12720struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012721 uint8_t cmd; /* RLIMIT_xxx fit into it */
12722 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012723 char option;
12724};
12725
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012726static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012727#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012728 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012729#endif
12730#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012731 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012732#endif
12733#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012734 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012735#endif
12736#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012737 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012738#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012739#ifdef RLIMIT_CORE
12740 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012741#endif
12742#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012743 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012744#endif
12745#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012746 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012747#endif
12748#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012749 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012750#endif
12751#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012752 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012753#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012754#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012755 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012756#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012757#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012758 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012759#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012760};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012761static const char limits_name[] =
12762#ifdef RLIMIT_CPU
12763 "time(seconds)" "\0"
12764#endif
12765#ifdef RLIMIT_FSIZE
12766 "file(blocks)" "\0"
12767#endif
12768#ifdef RLIMIT_DATA
12769 "data(kb)" "\0"
12770#endif
12771#ifdef RLIMIT_STACK
12772 "stack(kb)" "\0"
12773#endif
12774#ifdef RLIMIT_CORE
12775 "coredump(blocks)" "\0"
12776#endif
12777#ifdef RLIMIT_RSS
12778 "memory(kb)" "\0"
12779#endif
12780#ifdef RLIMIT_MEMLOCK
12781 "locked memory(kb)" "\0"
12782#endif
12783#ifdef RLIMIT_NPROC
12784 "process" "\0"
12785#endif
12786#ifdef RLIMIT_NOFILE
12787 "nofiles" "\0"
12788#endif
12789#ifdef RLIMIT_AS
12790 "vmemory(kb)" "\0"
12791#endif
12792#ifdef RLIMIT_LOCKS
12793 "locks" "\0"
12794#endif
12795;
Eric Andersenc470f442003-07-28 09:56:35 +000012796
Glenn L McGrath76620622004-01-13 10:19:37 +000012797enum limtype { SOFT = 0x1, HARD = 0x2 };
12798
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012799static void
12800printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012801 const struct limits *l)
12802{
12803 rlim_t val;
12804
12805 val = limit->rlim_max;
12806 if (how & SOFT)
12807 val = limit->rlim_cur;
12808
12809 if (val == RLIM_INFINITY)
12810 out1fmt("unlimited\n");
12811 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012812 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012813 out1fmt("%lld\n", (long long) val);
12814 }
12815}
12816
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012817static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012818ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012819{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012820 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012821 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012822 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012823 const struct limits *l;
12824 int set, all = 0;
12825 int optc, what;
12826 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012827
12828 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012829 while ((optc = nextopt("HSa"
12830#ifdef RLIMIT_CPU
12831 "t"
12832#endif
12833#ifdef RLIMIT_FSIZE
12834 "f"
12835#endif
12836#ifdef RLIMIT_DATA
12837 "d"
12838#endif
12839#ifdef RLIMIT_STACK
12840 "s"
12841#endif
12842#ifdef RLIMIT_CORE
12843 "c"
12844#endif
12845#ifdef RLIMIT_RSS
12846 "m"
12847#endif
12848#ifdef RLIMIT_MEMLOCK
12849 "l"
12850#endif
12851#ifdef RLIMIT_NPROC
12852 "p"
12853#endif
12854#ifdef RLIMIT_NOFILE
12855 "n"
12856#endif
12857#ifdef RLIMIT_AS
12858 "v"
12859#endif
12860#ifdef RLIMIT_LOCKS
12861 "w"
12862#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012863 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012864 switch (optc) {
12865 case 'H':
12866 how = HARD;
12867 break;
12868 case 'S':
12869 how = SOFT;
12870 break;
12871 case 'a':
12872 all = 1;
12873 break;
12874 default:
12875 what = optc;
12876 }
12877
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012878 for (l = limits_tbl; l->option != what; l++)
12879 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012880
12881 set = *argptr ? 1 : 0;
12882 if (set) {
12883 char *p = *argptr;
12884
12885 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012886 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012887 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012888 val = RLIM_INFINITY;
12889 else {
12890 val = (rlim_t) 0;
12891
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012892 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012893 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012894 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012895 if (val < (rlim_t) 0)
12896 break;
12897 }
12898 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012899 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012900 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012901 }
12902 }
12903 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012904 const char *lname = limits_name;
12905 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012906 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012907 out1fmt("%-20s ", lname);
12908 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012909 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012910 }
12911 return 0;
12912 }
12913
12914 getrlimit(l->cmd, &limit);
12915 if (set) {
12916 if (how & HARD)
12917 limit.rlim_max = val;
12918 if (how & SOFT)
12919 limit.rlim_cur = val;
12920 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012921 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012922 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012923 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012924 }
12925 return 0;
12926}
12927
Eric Andersen90898442003-08-06 11:20:52 +000012928
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012929/* ============ Math support */
12930
Denis Vlasenko131ae172007-02-18 13:00:19 +000012931#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012932
12933/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12934
12935 Permission is hereby granted, free of charge, to any person obtaining
12936 a copy of this software and associated documentation files (the
12937 "Software"), to deal in the Software without restriction, including
12938 without limitation the rights to use, copy, modify, merge, publish,
12939 distribute, sublicense, and/or sell copies of the Software, and to
12940 permit persons to whom the Software is furnished to do so, subject to
12941 the following conditions:
12942
12943 The above copyright notice and this permission notice shall be
12944 included in all copies or substantial portions of the Software.
12945
12946 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12947 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12948 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12949 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12950 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12951 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12952 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12953*/
12954
12955/* This is my infix parser/evaluator. It is optimized for size, intended
12956 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012957 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012958 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012959 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012960 * be that which POSIX specifies for shells. */
12961
12962/* The code uses a simple two-stack algorithm. See
12963 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012964 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012965 * this is based (this code differs in that it applies operators immediately
12966 * to the stack instead of adding them to a queue to end up with an
12967 * expression). */
12968
12969/* To use the routine, call it with an expression string and error return
12970 * pointer */
12971
12972/*
12973 * Aug 24, 2001 Manuel Novoa III
12974 *
12975 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12976 *
12977 * 1) In arith_apply():
12978 * a) Cached values of *numptr and &(numptr[-1]).
12979 * b) Removed redundant test for zero denominator.
12980 *
12981 * 2) In arith():
12982 * a) Eliminated redundant code for processing operator tokens by moving
12983 * to a table-based implementation. Also folded handling of parens
12984 * into the table.
12985 * b) Combined all 3 loops which called arith_apply to reduce generated
12986 * code size at the cost of speed.
12987 *
12988 * 3) The following expressions were treated as valid by the original code:
12989 * 1() , 0! , 1 ( *3 ) .
12990 * These bugs have been fixed by internally enclosing the expression in
12991 * parens and then checking that all binary ops and right parens are
12992 * preceded by a valid expression (NUM_TOKEN).
12993 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012994 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012995 * ctype's isspace() if it is used by another busybox applet or if additional
12996 * whitespace chars should be considered. Look below the "#include"s for a
12997 * precompiler test.
12998 */
12999
13000/*
13001 * Aug 26, 2001 Manuel Novoa III
13002 *
13003 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
13004 *
13005 * Merge in Aaron's comments previously posted to the busybox list,
13006 * modified slightly to take account of my changes to the code.
13007 *
13008 */
13009
13010/*
13011 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
13012 *
13013 * - allow access to variable,
13014 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
13015 * - realize assign syntax (VAR=expr, +=, *= etc)
13016 * - realize exponentiation (** operator)
13017 * - realize comma separated - expr, expr
13018 * - realise ++expr --expr expr++ expr--
13019 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000013020 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000013021 * - was restored loses XOR operator
13022 * - remove one goto label, added three ;-)
13023 * - protect $((num num)) as true zero expr (Manuel`s error)
13024 * - always use special isspace(), see comment from bash ;-)
13025 */
13026
Eric Andersen90898442003-08-06 11:20:52 +000013027#define arith_isspace(arithval) \
13028 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13029
Eric Andersen90898442003-08-06 11:20:52 +000013030typedef unsigned char operator;
13031
13032/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000013033 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000013034 * precedence. The ID portion is so that multiple operators can have the
13035 * same precedence, ensuring that the leftmost one is evaluated first.
13036 * Consider * and /. */
13037
13038#define tok_decl(prec,id) (((id)<<5)|(prec))
13039#define PREC(op) ((op) & 0x1F)
13040
13041#define TOK_LPAREN tok_decl(0,0)
13042
13043#define TOK_COMMA tok_decl(1,0)
13044
13045#define TOK_ASSIGN tok_decl(2,0)
13046#define TOK_AND_ASSIGN tok_decl(2,1)
13047#define TOK_OR_ASSIGN tok_decl(2,2)
13048#define TOK_XOR_ASSIGN tok_decl(2,3)
13049#define TOK_PLUS_ASSIGN tok_decl(2,4)
13050#define TOK_MINUS_ASSIGN tok_decl(2,5)
13051#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13052#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13053
13054#define TOK_MUL_ASSIGN tok_decl(3,0)
13055#define TOK_DIV_ASSIGN tok_decl(3,1)
13056#define TOK_REM_ASSIGN tok_decl(3,2)
13057
13058/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013059#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000013060
13061/* conditional is right associativity too */
13062#define TOK_CONDITIONAL tok_decl(4,0)
13063#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13064
13065#define TOK_OR tok_decl(5,0)
13066
13067#define TOK_AND tok_decl(6,0)
13068
13069#define TOK_BOR tok_decl(7,0)
13070
13071#define TOK_BXOR tok_decl(8,0)
13072
13073#define TOK_BAND tok_decl(9,0)
13074
13075#define TOK_EQ tok_decl(10,0)
13076#define TOK_NE tok_decl(10,1)
13077
13078#define TOK_LT tok_decl(11,0)
13079#define TOK_GT tok_decl(11,1)
13080#define TOK_GE tok_decl(11,2)
13081#define TOK_LE tok_decl(11,3)
13082
13083#define TOK_LSHIFT tok_decl(12,0)
13084#define TOK_RSHIFT tok_decl(12,1)
13085
13086#define TOK_ADD tok_decl(13,0)
13087#define TOK_SUB tok_decl(13,1)
13088
13089#define TOK_MUL tok_decl(14,0)
13090#define TOK_DIV tok_decl(14,1)
13091#define TOK_REM tok_decl(14,2)
13092
13093/* exponent is right associativity */
13094#define TOK_EXPONENT tok_decl(15,1)
13095
13096/* For now unary operators. */
13097#define UNARYPREC 16
13098#define TOK_BNOT tok_decl(UNARYPREC,0)
13099#define TOK_NOT tok_decl(UNARYPREC,1)
13100
13101#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13102#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13103
13104#define PREC_PRE (UNARYPREC+2)
13105
13106#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13107#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13108
13109#define PREC_POST (UNARYPREC+3)
13110
13111#define TOK_POST_INC tok_decl(PREC_POST, 0)
13112#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13113
13114#define SPEC_PREC (UNARYPREC+4)
13115
13116#define TOK_NUM tok_decl(SPEC_PREC, 0)
13117#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13118
13119#define NUMPTR (*numstackptr)
13120
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013121static int
13122tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013123{
13124 operator prec = PREC(op);
13125
13126 convert_prec_is_assing(prec);
13127 return (prec == PREC(TOK_ASSIGN) ||
13128 prec == PREC_PRE || prec == PREC_POST);
13129}
13130
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013131static int
13132is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013133{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013134 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13135 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013136}
13137
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013138typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013139 arith_t val;
13140 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013141 char contidional_second_val_initialized;
13142 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013143 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013144} v_n_t;
13145
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013146typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013147 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013148 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013149} chk_var_recursive_looped_t;
13150
13151static chk_var_recursive_looped_t *prev_chk_var_recursive;
13152
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013153static int
13154arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013155{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013156 if (t->var) {
13157 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013158
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013159 if (p) {
13160 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013161
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013162 /* recursive try as expression */
13163 chk_var_recursive_looped_t *cur;
13164 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013165
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013166 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13167 if (strcmp(cur->var, t->var) == 0) {
13168 /* expression recursion loop detected */
13169 return -5;
13170 }
13171 }
13172 /* save current lookuped var name */
13173 cur = prev_chk_var_recursive;
13174 cur_save.var = t->var;
13175 cur_save.next = cur;
13176 prev_chk_var_recursive = &cur_save;
13177
13178 t->val = arith (p, &errcode);
13179 /* restore previous ptr after recursiving */
13180 prev_chk_var_recursive = cur;
13181 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013182 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013183 /* allow undefined var as 0 */
13184 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013185 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013186 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013187}
13188
13189/* "applying" a token means performing it on the top elements on the integer
13190 * stack. For a unary operator it will only change the top element, but a
13191 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013192static int
13193arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013194{
Eric Andersen90898442003-08-06 11:20:52 +000013195 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013196 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013197 int ret_arith_lookup_val;
13198
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013199 /* There is no operator that can work without arguments */
13200 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013201 numptr_m1 = NUMPTR - 1;
13202
13203 /* check operand is var with noninteger value */
13204 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013205 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013206 return ret_arith_lookup_val;
13207
13208 rez = numptr_m1->val;
13209 if (op == TOK_UMINUS)
13210 rez *= -1;
13211 else if (op == TOK_NOT)
13212 rez = !rez;
13213 else if (op == TOK_BNOT)
13214 rez = ~rez;
13215 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13216 rez++;
13217 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13218 rez--;
13219 else if (op != TOK_UPLUS) {
13220 /* Binary operators */
13221
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013222 /* check and binary operators need two arguments */
13223 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013224
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013225 /* ... and they pop one */
13226 --NUMPTR;
13227 numptr_val = rez;
13228 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013229 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013230 /* protect $((expr1 ? expr2)) without ": expr" */
13231 goto err;
13232 }
13233 rez = numptr_m1->contidional_second_val;
13234 } else if (numptr_m1->contidional_second_val_initialized) {
13235 /* protect $((expr1 : expr2)) without "expr ? " */
13236 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013237 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013238 numptr_m1 = NUMPTR - 1;
13239 if (op != TOK_ASSIGN) {
13240 /* check operand is var with noninteger value for not '=' */
13241 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13242 if (ret_arith_lookup_val)
13243 return ret_arith_lookup_val;
13244 }
13245 if (op == TOK_CONDITIONAL) {
13246 numptr_m1->contidional_second_val = rez;
13247 }
13248 rez = numptr_m1->val;
13249 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013250 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013251 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013252 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013253 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013254 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013255 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013256 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013257 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013258 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013259 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013260 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013261 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013262 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013263 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013264 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013265 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013266 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013267 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013268 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013269 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013270 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013271 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013272 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013273 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013274 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013275 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013276 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013277 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013278 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013279 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013280 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013281 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013282 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013283 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013284 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013285 /* protect $((expr : expr)) without "expr ? " */
13286 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013287 }
13288 numptr_m1->contidional_second_val_initialized = op;
13289 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013290 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013291 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013292 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013293 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013294 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013295 return -3; /* exponent less than 0 */
13296 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013297 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013298
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013299 if (numptr_val)
13300 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013301 c *= rez;
13302 rez = c;
13303 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013304 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013305 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013306 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013307 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013308 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013309 rez %= numptr_val;
13310 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013311 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013312 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013313
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013314 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013315 /* Hmm, 1=2 ? */
13316 goto err;
13317 }
13318 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013319#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013320 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013321#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013322 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013323#endif
Eric Andersen90898442003-08-06 11:20:52 +000013324 setvar(numptr_m1->var, buf, 0);
13325 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013326 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013327 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013328 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013329 rez++;
13330 }
13331 numptr_m1->val = rez;
13332 /* protect geting var value, is number now */
13333 numptr_m1->var = NULL;
13334 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013335 err:
13336 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013337}
13338
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013339/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013340static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013341 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13342 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13343 '<','<', 0, TOK_LSHIFT,
13344 '>','>', 0, TOK_RSHIFT,
13345 '|','|', 0, TOK_OR,
13346 '&','&', 0, TOK_AND,
13347 '!','=', 0, TOK_NE,
13348 '<','=', 0, TOK_LE,
13349 '>','=', 0, TOK_GE,
13350 '=','=', 0, TOK_EQ,
13351 '|','=', 0, TOK_OR_ASSIGN,
13352 '&','=', 0, TOK_AND_ASSIGN,
13353 '*','=', 0, TOK_MUL_ASSIGN,
13354 '/','=', 0, TOK_DIV_ASSIGN,
13355 '%','=', 0, TOK_REM_ASSIGN,
13356 '+','=', 0, TOK_PLUS_ASSIGN,
13357 '-','=', 0, TOK_MINUS_ASSIGN,
13358 '-','-', 0, TOK_POST_DEC,
13359 '^','=', 0, TOK_XOR_ASSIGN,
13360 '+','+', 0, TOK_POST_INC,
13361 '*','*', 0, TOK_EXPONENT,
13362 '!', 0, TOK_NOT,
13363 '<', 0, TOK_LT,
13364 '>', 0, TOK_GT,
13365 '=', 0, TOK_ASSIGN,
13366 '|', 0, TOK_BOR,
13367 '&', 0, TOK_BAND,
13368 '*', 0, TOK_MUL,
13369 '/', 0, TOK_DIV,
13370 '%', 0, TOK_REM,
13371 '+', 0, TOK_ADD,
13372 '-', 0, TOK_SUB,
13373 '^', 0, TOK_BXOR,
13374 /* uniq */
13375 '~', 0, TOK_BNOT,
13376 ',', 0, TOK_COMMA,
13377 '?', 0, TOK_CONDITIONAL,
13378 ':', 0, TOK_CONDITIONAL_SEP,
13379 ')', 0, TOK_RPAREN,
13380 '(', 0, TOK_LPAREN,
13381 0
13382};
13383/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013384#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013385
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013386static arith_t
13387arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013388{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013389 char arithval; /* Current character under analysis */
13390 operator lasttok, op;
13391 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013392 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013393 const char *p = endexpression;
13394 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013395 v_n_t *numstack, *numstackptr;
13396 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013397
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013398 /* Stack of integers */
13399 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13400 * in any given correct or incorrect expression is left as an exercise to
13401 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013402 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013403 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013404 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013405
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013406 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13407 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013408
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013409 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013410 arithval = *expr;
13411 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013412 if (p == endexpression) {
13413 /* Null expression. */
13414 return 0;
13415 }
13416
13417 /* This is only reached after all tokens have been extracted from the
13418 * input stream. If there are still tokens on the operator stack, they
13419 * are to be applied in order. At the end, there should be a final
13420 * result on the integer stack */
13421
13422 if (expr != endexpression + 1) {
13423 /* If we haven't done so already, */
13424 /* append a closing right paren */
13425 expr = endexpression;
13426 /* and let the loop process it. */
13427 continue;
13428 }
13429 /* At this point, we're done with the expression. */
13430 if (numstackptr != numstack+1) {
13431 /* ... but if there isn't, it's bad */
13432 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013433 *perrcode = -1;
13434 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013435 }
13436 if (numstack->var) {
13437 /* expression is $((var)) only, lookup now */
13438 errcode = arith_lookup_val(numstack);
13439 }
13440 ret:
13441 *perrcode = errcode;
13442 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013443 }
13444
Eric Andersen90898442003-08-06 11:20:52 +000013445 /* Continue processing the expression. */
13446 if (arith_isspace(arithval)) {
13447 /* Skip whitespace */
13448 goto prologue;
13449 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013450 p = endofname(expr);
13451 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013452 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013453
13454 numstackptr->var = alloca(var_name_size);
13455 safe_strncpy(numstackptr->var, expr, var_name_size);
13456 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013457 num:
Eric Andersen90898442003-08-06 11:20:52 +000013458 numstackptr->contidional_second_val_initialized = 0;
13459 numstackptr++;
13460 lasttok = TOK_NUM;
13461 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013462 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013463 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013464 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013465#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013466 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013467#else
13468 numstackptr->val = strtol(expr, (char **) &expr, 0);
13469#endif
Eric Andersen90898442003-08-06 11:20:52 +000013470 goto num;
13471 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013472 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013473 const char *o;
13474
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013475 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013476 /* strange operator not found */
13477 goto err;
13478 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013479 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013480 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013481 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013482 /* found */
13483 expr = o - 1;
13484 break;
13485 }
13486 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013487 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013488 p++;
13489 /* skip zero delim */
13490 p++;
13491 }
13492 op = p[1];
13493
13494 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013495 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13496 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013497
13498 /* Plus and minus are binary (not unary) _only_ if the last
13499 * token was as number, or a right paren (which pretends to be
13500 * a number, since it evaluates to one). Think about it.
13501 * It makes sense. */
13502 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013503 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013504 case TOK_ADD:
13505 op = TOK_UPLUS;
13506 break;
13507 case TOK_SUB:
13508 op = TOK_UMINUS;
13509 break;
13510 case TOK_POST_INC:
13511 op = TOK_PRE_INC;
13512 break;
13513 case TOK_POST_DEC:
13514 op = TOK_PRE_DEC;
13515 break;
Eric Andersen90898442003-08-06 11:20:52 +000013516 }
13517 }
13518 /* We don't want a unary operator to cause recursive descent on the
13519 * stack, because there can be many in a row and it could cause an
13520 * operator to be evaluated before its argument is pushed onto the
13521 * integer stack. */
13522 /* But for binary operators, "apply" everything on the operator
13523 * stack until we find an operator with a lesser priority than the
13524 * one we have just extracted. */
13525 /* Left paren is given the lowest priority so it will never be
13526 * "applied" in this way.
13527 * if associativity is right and priority eq, applied also skip
13528 */
13529 prec = PREC(op);
13530 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13531 /* not left paren or unary */
13532 if (lasttok != TOK_NUM) {
13533 /* binary op must be preceded by a num */
13534 goto err;
13535 }
13536 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013537 if (op == TOK_RPAREN) {
13538 /* The algorithm employed here is simple: while we don't
13539 * hit an open paren nor the bottom of the stack, pop
13540 * tokens and apply them */
13541 if (stackptr[-1] == TOK_LPAREN) {
13542 --stackptr;
13543 /* Any operator directly after a */
13544 lasttok = TOK_NUM;
13545 /* close paren should consider itself binary */
13546 goto prologue;
13547 }
13548 } else {
13549 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013550
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013551 convert_prec_is_assing(prec);
13552 convert_prec_is_assing(prev_prec);
13553 if (prev_prec < prec)
13554 break;
13555 /* check right assoc */
13556 if (prev_prec == prec && is_right_associativity(prec))
13557 break;
13558 }
13559 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13560 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013561 }
13562 if (op == TOK_RPAREN) {
13563 goto err;
13564 }
13565 }
13566
13567 /* Push this operator to the stack and remember it. */
13568 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013569 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013570 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013571 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013572}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013573#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013574
13575
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013576/* ============ main() and helpers */
13577
13578/*
13579 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013580 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013581static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013582static void
13583exitshell(void)
13584{
13585 struct jmploc loc;
13586 char *p;
13587 int status;
13588
13589 status = exitstatus;
13590 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13591 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013592 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013593/* dash bug: it just does _exit(exitstatus) here
13594 * but we have to do setjobctl(0) first!
13595 * (bug is still not fixed in dash-0.5.3 - if you run dash
13596 * under Midnight Commander, on exit from dash MC is backgrounded) */
13597 status = exitstatus;
13598 goto out;
13599 }
13600 exception_handler = &loc;
13601 p = trap[0];
13602 if (p) {
13603 trap[0] = NULL;
13604 evalstring(p, 0);
13605 }
13606 flush_stdout_stderr();
13607 out:
13608 setjobctl(0);
13609 _exit(status);
13610 /* NOTREACHED */
13611}
13612
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013613static void
13614init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013615{
13616 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013617 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013618
13619 /* from trap.c: */
13620 signal(SIGCHLD, SIG_DFL);
13621
13622 /* from var.c: */
13623 {
13624 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013625 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013626 const char *p;
13627 struct stat st1, st2;
13628
13629 initvar();
13630 for (envp = environ; envp && *envp; envp++) {
13631 if (strchr(*envp, '=')) {
13632 setvareq(*envp, VEXPORT|VTEXTFIXED);
13633 }
13634 }
13635
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013636 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013637 setvar("PPID", ppid, 0);
13638
13639 p = lookupvar("PWD");
13640 if (p)
13641 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13642 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13643 p = '\0';
13644 setpwd(p, 0);
13645 }
13646}
13647
13648/*
13649 * Process the shell command line arguments.
13650 */
13651static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013652procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013653{
13654 int i;
13655 const char *xminusc;
13656 char **xargv;
13657
13658 xargv = argv;
13659 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013660 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013661 xargv++;
13662 for (i = 0; i < NOPTS; i++)
13663 optlist[i] = 2;
13664 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013665 if (options(1)) {
13666 /* it already printed err message */
13667 raise_exception(EXERROR);
13668 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013669 xargv = argptr;
13670 xminusc = minusc;
13671 if (*xargv == NULL) {
13672 if (xminusc)
13673 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13674 sflag = 1;
13675 }
13676 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13677 iflag = 1;
13678 if (mflag == 2)
13679 mflag = iflag;
13680 for (i = 0; i < NOPTS; i++)
13681 if (optlist[i] == 2)
13682 optlist[i] = 0;
13683#if DEBUG == 2
13684 debug = 1;
13685#endif
13686 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13687 if (xminusc) {
13688 minusc = *xargv++;
13689 if (*xargv)
13690 goto setarg0;
13691 } else if (!sflag) {
13692 setinputfile(*xargv, 0);
13693 setarg0:
13694 arg0 = *xargv++;
13695 commandname = arg0;
13696 }
13697
13698 shellparam.p = xargv;
13699#if ENABLE_ASH_GETOPTS
13700 shellparam.optind = 1;
13701 shellparam.optoff = -1;
13702#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013703 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013704 while (*xargv) {
13705 shellparam.nparam++;
13706 xargv++;
13707 }
13708 optschanged();
13709}
13710
13711/*
13712 * Read /etc/profile or .profile.
13713 */
13714static void
13715read_profile(const char *name)
13716{
13717 int skip;
13718
13719 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13720 return;
13721 skip = cmdloop(0);
13722 popfile();
13723 if (skip)
13724 exitshell();
13725}
13726
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013727/*
13728 * This routine is called when an error or an interrupt occurs in an
13729 * interactive shell and control is returned to the main command loop.
13730 */
13731static void
13732reset(void)
13733{
13734 /* from eval.c: */
13735 evalskip = 0;
13736 loopnest = 0;
13737 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013738 g_parsefile->left_in_buffer = 0;
13739 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013740 popallfiles();
13741 /* from parser.c: */
13742 tokpushback = 0;
13743 checkkwd = 0;
13744 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013745 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013746}
13747
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013748#if PROFILE
13749static short profile_buf[16384];
13750extern int etext();
13751#endif
13752
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013753/*
13754 * Main routine. We initialize things, parse the arguments, execute
13755 * profiles if we're a login shell, and then call cmdloop to execute
13756 * commands. The setjmp call sets up the location to jump to when an
13757 * exception occurs. When an exception occurs the variable "state"
13758 * is used to figure out how far we had gotten.
13759 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013760int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013761int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013762{
13763 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013764 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013765 struct jmploc jmploc;
13766 struct stackmark smark;
13767
Denis Vlasenko01631112007-12-16 17:20:38 +000013768 /* Initialize global data */
13769 INIT_G_misc();
13770 INIT_G_memstack();
13771 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013772#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013773 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013774#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013775 INIT_G_cmdtable();
13776
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013777#if PROFILE
13778 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13779#endif
13780
13781#if ENABLE_FEATURE_EDITING
13782 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13783#endif
13784 state = 0;
13785 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013786 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013787 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013788
13789 reset();
13790
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013791 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013792 if (e == EXERROR)
13793 exitstatus = 2;
13794 s = state;
13795 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13796 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013797 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013798 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013799
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013800 popstackmark(&smark);
13801 FORCE_INT_ON; /* enable interrupts */
13802 if (s == 1)
13803 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013804 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013805 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013806 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013807 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013808 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013809 }
13810 exception_handler = &jmploc;
13811#if DEBUG
13812 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013813 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013814 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013815#endif
13816 rootpid = getpid();
13817
13818#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013819 /* Can use monotonic_ns() for better randomness but for now it is
13820 * not used anywhere else in busybox... so avoid bloat */
13821 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013822#endif
13823 init();
13824 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013825 procargs(argv);
13826
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013827#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13828 if (iflag) {
13829 const char *hp = lookupvar("HISTFILE");
13830
13831 if (hp == NULL) {
13832 hp = lookupvar("HOME");
13833 if (hp != NULL) {
13834 char *defhp = concat_path_file(hp, ".ash_history");
13835 setvar("HISTFILE", defhp, 0);
13836 free(defhp);
13837 }
13838 }
13839 }
13840#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013841 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013842 isloginsh = 1;
13843 if (isloginsh) {
13844 state = 1;
13845 read_profile("/etc/profile");
13846 state1:
13847 state = 2;
13848 read_profile(".profile");
13849 }
13850 state2:
13851 state = 3;
13852 if (
13853#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013854 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013855#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013856 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013857 ) {
13858 shinit = lookupvar("ENV");
13859 if (shinit != NULL && *shinit != '\0') {
13860 read_profile(shinit);
13861 }
13862 }
13863 state3:
13864 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013865 if (minusc) {
13866 /* evalstring pushes parsefile stack.
13867 * Ensure we don't falsely claim that 0 (stdin)
13868 * is one of stacked source fds */
13869 if (!sflag)
13870 g_parsefile->fd = -1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013871 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013872 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013873
13874 if (sflag || minusc == NULL) {
13875#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013876 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013877 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013878 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013879 line_input_state->hist_file = hp;
13880 }
13881#endif
13882 state4: /* XXX ??? - why isn't this before the "if" statement */
13883 cmdloop(1);
13884 }
13885#if PROFILE
13886 monitor(0);
13887#endif
13888#ifdef GPROF
13889 {
13890 extern void _mcleanup(void);
13891 _mcleanup();
13892 }
13893#endif
13894 exitshell();
13895 /* NOTREACHED */
13896}
13897
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013898
Eric Andersendf82f612001-06-28 07:46:40 +000013899/*-
13900 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013901 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013902 *
13903 * This code is derived from software contributed to Berkeley by
13904 * Kenneth Almquist.
13905 *
13906 * Redistribution and use in source and binary forms, with or without
13907 * modification, are permitted provided that the following conditions
13908 * are met:
13909 * 1. Redistributions of source code must retain the above copyright
13910 * notice, this list of conditions and the following disclaimer.
13911 * 2. Redistributions in binary form must reproduce the above copyright
13912 * notice, this list of conditions and the following disclaimer in the
13913 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013914 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013915 * may be used to endorse or promote products derived from this software
13916 * without specific prior written permission.
13917 *
13918 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13919 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13920 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13921 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13922 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13923 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13924 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13925 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13926 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13927 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13928 * SUCH DAMAGE.
13929 */