blob: a799cb1a6a00f13e13d01118c07ae0a6be091ff7 [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 Vlasenkof1733952009-03-19 23:21:55 +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
Denis Vlasenkof1733952009-03-19 23:21:55 +0000132 * jump to when an exception occurs, and the global variable exception_type
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));
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003837 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3838 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003839 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003841
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842 INT_OFF;
3843 thisjob = NULL;
3844 for (jp = curjob; jp; jp = jp->prev_job) {
3845 struct procstat *sp;
3846 struct procstat *spend;
3847 if (jp->state == JOBDONE)
3848 continue;
3849 state = JOBDONE;
3850 spend = jp->ps + jp->nprocs;
3851 sp = jp->ps;
3852 do {
3853 if (sp->pid == pid) {
3854 TRACE(("Job %d: changing status of proc %d "
3855 "from 0x%x to 0x%x\n",
3856 jobno(jp), pid, sp->status, status));
3857 sp->status = status;
3858 thisjob = jp;
3859 }
3860 if (sp->status == -1)
3861 state = JOBRUNNING;
3862#if JOBS
3863 if (state == JOBRUNNING)
3864 continue;
3865 if (WIFSTOPPED(sp->status)) {
3866 jp->stopstatus = sp->status;
3867 state = JOBSTOPPED;
3868 }
3869#endif
3870 } while (++sp < spend);
3871 if (thisjob)
3872 goto gotjob;
3873 }
3874#if JOBS
3875 if (!WIFSTOPPED(status))
3876#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003877 jobless--;
3878 goto out;
3879
3880 gotjob:
3881 if (state != JOBRUNNING) {
3882 thisjob->changed = 1;
3883
3884 if (thisjob->state != state) {
3885 TRACE(("Job %d: changing state from %d to %d\n",
3886 jobno(thisjob), thisjob->state, state));
3887 thisjob->state = state;
3888#if JOBS
3889 if (state == JOBSTOPPED) {
3890 set_curjob(thisjob, CUR_STOPPED);
3891 }
3892#endif
3893 }
3894 }
3895
3896 out:
3897 INT_ON;
3898
3899 if (thisjob && thisjob == job) {
3900 char s[48 + 1];
3901 int len;
3902
3903 len = sprint_status(s, status, 1);
3904 if (len) {
3905 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003906 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003907 out2str(s);
3908 }
3909 }
3910 return pid;
3911}
3912
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003913static int
3914blocking_wait_with_raise_on_sig(struct job *job)
3915{
3916 pid_t pid = dowait(DOWAIT_BLOCK, job);
3917 if (pid <= 0 && pendingsig)
3918 raise_exception(EXSIG);
3919 return pid;
3920}
3921
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003922#if JOBS
3923static void
3924showjob(FILE *out, struct job *jp, int mode)
3925{
3926 struct procstat *ps;
3927 struct procstat *psend;
3928 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003929 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003930 char s[80];
3931
3932 ps = jp->ps;
3933
3934 if (mode & SHOW_PGID) {
3935 /* just output process (group) id of pipeline */
3936 fprintf(out, "%d\n", ps->pid);
3937 return;
3938 }
3939
3940 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003941 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942
3943 if (jp == curjob)
3944 s[col - 2] = '+';
3945 else if (curjob && jp == curjob->prev_job)
3946 s[col - 2] = '-';
3947
3948 if (mode & SHOW_PID)
3949 col += fmtstr(s + col, 16, "%d ", ps->pid);
3950
3951 psend = ps + jp->nprocs;
3952
3953 if (jp->state == JOBRUNNING) {
3954 strcpy(s + col, "Running");
3955 col += sizeof("Running") - 1;
3956 } else {
3957 int status = psend[-1].status;
3958 if (jp->state == JOBSTOPPED)
3959 status = jp->stopstatus;
3960 col += sprint_status(s + col, status, 0);
3961 }
3962
3963 goto start;
3964
3965 do {
3966 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003967 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 start:
3969 fprintf(out, "%s%*c%s",
3970 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3971 );
3972 if (!(mode & SHOW_PID)) {
3973 showpipe(jp, out);
3974 break;
3975 }
3976 if (++ps == psend) {
3977 outcslow('\n', out);
3978 break;
3979 }
3980 } while (1);
3981
3982 jp->changed = 0;
3983
3984 if (jp->state == JOBDONE) {
3985 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3986 freejob(jp);
3987 }
3988}
3989
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003990/*
3991 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3992 * statuses have changed since the last call to showjobs.
3993 */
3994static void
3995showjobs(FILE *out, int mode)
3996{
3997 struct job *jp;
3998
3999 TRACE(("showjobs(%x) called\n", mode));
4000
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004001 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004002 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004003 continue;
4004
4005 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004006 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004007 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004008 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004009 }
4010}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004011
4012static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004013jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004014{
4015 int mode, m;
4016
4017 mode = 0;
4018 while ((m = nextopt("lp"))) {
4019 if (m == 'l')
4020 mode = SHOW_PID;
4021 else
4022 mode = SHOW_PGID;
4023 }
4024
4025 argv = argptr;
4026 if (*argv) {
4027 do
4028 showjob(stdout, getjob(*argv,0), mode);
4029 while (*++argv);
4030 } else
4031 showjobs(stdout, mode);
4032
4033 return 0;
4034}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035#endif /* JOBS */
4036
4037static int
4038getstatus(struct job *job)
4039{
4040 int status;
4041 int retval;
4042
4043 status = job->ps[job->nprocs - 1].status;
4044 retval = WEXITSTATUS(status);
4045 if (!WIFEXITED(status)) {
4046#if JOBS
4047 retval = WSTOPSIG(status);
4048 if (!WIFSTOPPED(status))
4049#endif
4050 {
4051 /* XXX: limits number of signals */
4052 retval = WTERMSIG(status);
4053#if JOBS
4054 if (retval == SIGINT)
4055 job->sigint = 1;
4056#endif
4057 }
4058 retval += 128;
4059 }
4060 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4061 jobno(job), job->nprocs, status, retval));
4062 return retval;
4063}
4064
4065static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004066waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004067{
4068 struct job *job;
4069 int retval;
4070 struct job *jp;
4071
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004072// exsig++;
4073// xbarrier();
4074 if (pendingsig)
4075 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076
4077 nextopt(nullstr);
4078 retval = 0;
4079
4080 argv = argptr;
4081 if (!*argv) {
4082 /* wait for all jobs */
4083 for (;;) {
4084 jp = curjob;
4085 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004086 if (!jp) /* no running procs */
4087 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088 if (jp->state == JOBRUNNING)
4089 break;
4090 jp->waited = 1;
4091 jp = jp->prev_job;
4092 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004093 /* man bash:
4094 * "When bash is waiting for an asynchronous command via
4095 * the wait builtin, the reception of a signal for which a trap
4096 * has been set will cause the wait builtin to return immediately
4097 * with an exit status greater than 128, immediately after which
4098 * the trap is executed."
4099 * Do we do it that way? */
4100 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004101 }
4102 }
4103
4104 retval = 127;
4105 do {
4106 if (**argv != '%') {
4107 pid_t pid = number(*argv);
4108 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004109 while (1) {
4110 if (!job)
4111 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004112 if (job->ps[job->nprocs - 1].pid == pid)
4113 break;
4114 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004115 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004116 } else
4117 job = getjob(*argv, 0);
4118 /* loop until process terminated or stopped */
4119 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004120 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121 job->waited = 1;
4122 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004123 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004124 } while (*++argv);
4125
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004126 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004127 return retval;
4128}
4129
4130static struct job *
4131growjobtab(void)
4132{
4133 size_t len;
4134 ptrdiff_t offset;
4135 struct job *jp, *jq;
4136
4137 len = njobs * sizeof(*jp);
4138 jq = jobtab;
4139 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4140
4141 offset = (char *)jp - (char *)jq;
4142 if (offset) {
4143 /* Relocate pointers */
4144 size_t l = len;
4145
4146 jq = (struct job *)((char *)jq + l);
4147 while (l) {
4148 l -= sizeof(*jp);
4149 jq--;
4150#define joff(p) ((struct job *)((char *)(p) + l))
4151#define jmove(p) (p) = (void *)((char *)(p) + offset)
4152 if (joff(jp)->ps == &jq->ps0)
4153 jmove(joff(jp)->ps);
4154 if (joff(jp)->prev_job)
4155 jmove(joff(jp)->prev_job);
4156 }
4157 if (curjob)
4158 jmove(curjob);
4159#undef joff
4160#undef jmove
4161 }
4162
4163 njobs += 4;
4164 jobtab = jp;
4165 jp = (struct job *)((char *)jp + len);
4166 jq = jp + 3;
4167 do {
4168 jq->used = 0;
4169 } while (--jq >= jp);
4170 return jp;
4171}
4172
4173/*
4174 * Return a new job structure.
4175 * Called with interrupts off.
4176 */
4177static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004178makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179{
4180 int i;
4181 struct job *jp;
4182
4183 for (i = njobs, jp = jobtab; ; jp++) {
4184 if (--i < 0) {
4185 jp = growjobtab();
4186 break;
4187 }
4188 if (jp->used == 0)
4189 break;
4190 if (jp->state != JOBDONE || !jp->waited)
4191 continue;
4192#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004193 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004194 continue;
4195#endif
4196 freejob(jp);
4197 break;
4198 }
4199 memset(jp, 0, sizeof(*jp));
4200#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004201 /* jp->jobctl is a bitfield.
4202 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004203 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204 jp->jobctl = 1;
4205#endif
4206 jp->prev_job = curjob;
4207 curjob = jp;
4208 jp->used = 1;
4209 jp->ps = &jp->ps0;
4210 if (nprocs > 1) {
4211 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4212 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004213 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 jobno(jp)));
4215 return jp;
4216}
4217
4218#if JOBS
4219/*
4220 * Return a string identifying a command (to be printed by the
4221 * jobs command).
4222 */
4223static char *cmdnextc;
4224
4225static void
4226cmdputs(const char *s)
4227{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004228 static const char vstype[VSTYPE + 1][3] = {
4229 "", "}", "-", "+", "?", "=",
4230 "%", "%%", "#", "##"
4231 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4232 };
4233
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004234 const char *p, *str;
4235 char c, cc[2] = " ";
4236 char *nextc;
4237 int subtype = 0;
4238 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004239
4240 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4241 p = s;
4242 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004243 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004244 switch (c) {
4245 case CTLESC:
4246 c = *p++;
4247 break;
4248 case CTLVAR:
4249 subtype = *p++;
4250 if ((subtype & VSTYPE) == VSLENGTH)
4251 str = "${#";
4252 else
4253 str = "${";
4254 if (!(subtype & VSQUOTE) == !(quoted & 1))
4255 goto dostr;
4256 quoted ^= 1;
4257 c = '"';
4258 break;
4259 case CTLENDVAR:
4260 str = "\"}" + !(quoted & 1);
4261 quoted >>= 1;
4262 subtype = 0;
4263 goto dostr;
4264 case CTLBACKQ:
4265 str = "$(...)";
4266 goto dostr;
4267 case CTLBACKQ+CTLQUOTE:
4268 str = "\"$(...)\"";
4269 goto dostr;
4270#if ENABLE_ASH_MATH_SUPPORT
4271 case CTLARI:
4272 str = "$((";
4273 goto dostr;
4274 case CTLENDARI:
4275 str = "))";
4276 goto dostr;
4277#endif
4278 case CTLQUOTEMARK:
4279 quoted ^= 1;
4280 c = '"';
4281 break;
4282 case '=':
4283 if (subtype == 0)
4284 break;
4285 if ((subtype & VSTYPE) != VSNORMAL)
4286 quoted <<= 1;
4287 str = vstype[subtype & VSTYPE];
4288 if (subtype & VSNUL)
4289 c = ':';
4290 else
4291 goto checkstr;
4292 break;
4293 case '\'':
4294 case '\\':
4295 case '"':
4296 case '$':
4297 /* These can only happen inside quotes */
4298 cc[0] = c;
4299 str = cc;
4300 c = '\\';
4301 break;
4302 default:
4303 break;
4304 }
4305 USTPUTC(c, nextc);
4306 checkstr:
4307 if (!str)
4308 continue;
4309 dostr:
4310 while ((c = *str++)) {
4311 USTPUTC(c, nextc);
4312 }
4313 }
4314 if (quoted & 1) {
4315 USTPUTC('"', nextc);
4316 }
4317 *nextc = 0;
4318 cmdnextc = nextc;
4319}
4320
4321/* cmdtxt() and cmdlist() call each other */
4322static void cmdtxt(union node *n);
4323
4324static void
4325cmdlist(union node *np, int sep)
4326{
4327 for (; np; np = np->narg.next) {
4328 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004329 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004330 cmdtxt(np);
4331 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004332 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004333 }
4334}
4335
4336static void
4337cmdtxt(union node *n)
4338{
4339 union node *np;
4340 struct nodelist *lp;
4341 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004342
4343 if (!n)
4344 return;
4345 switch (n->type) {
4346 default:
4347#if DEBUG
4348 abort();
4349#endif
4350 case NPIPE:
4351 lp = n->npipe.cmdlist;
4352 for (;;) {
4353 cmdtxt(lp->n);
4354 lp = lp->next;
4355 if (!lp)
4356 break;
4357 cmdputs(" | ");
4358 }
4359 break;
4360 case NSEMI:
4361 p = "; ";
4362 goto binop;
4363 case NAND:
4364 p = " && ";
4365 goto binop;
4366 case NOR:
4367 p = " || ";
4368 binop:
4369 cmdtxt(n->nbinary.ch1);
4370 cmdputs(p);
4371 n = n->nbinary.ch2;
4372 goto donode;
4373 case NREDIR:
4374 case NBACKGND:
4375 n = n->nredir.n;
4376 goto donode;
4377 case NNOT:
4378 cmdputs("!");
4379 n = n->nnot.com;
4380 donode:
4381 cmdtxt(n);
4382 break;
4383 case NIF:
4384 cmdputs("if ");
4385 cmdtxt(n->nif.test);
4386 cmdputs("; then ");
4387 n = n->nif.ifpart;
4388 if (n->nif.elsepart) {
4389 cmdtxt(n);
4390 cmdputs("; else ");
4391 n = n->nif.elsepart;
4392 }
4393 p = "; fi";
4394 goto dotail;
4395 case NSUBSHELL:
4396 cmdputs("(");
4397 n = n->nredir.n;
4398 p = ")";
4399 goto dotail;
4400 case NWHILE:
4401 p = "while ";
4402 goto until;
4403 case NUNTIL:
4404 p = "until ";
4405 until:
4406 cmdputs(p);
4407 cmdtxt(n->nbinary.ch1);
4408 n = n->nbinary.ch2;
4409 p = "; done";
4410 dodo:
4411 cmdputs("; do ");
4412 dotail:
4413 cmdtxt(n);
4414 goto dotail2;
4415 case NFOR:
4416 cmdputs("for ");
4417 cmdputs(n->nfor.var);
4418 cmdputs(" in ");
4419 cmdlist(n->nfor.args, 1);
4420 n = n->nfor.body;
4421 p = "; done";
4422 goto dodo;
4423 case NDEFUN:
4424 cmdputs(n->narg.text);
4425 p = "() { ... }";
4426 goto dotail2;
4427 case NCMD:
4428 cmdlist(n->ncmd.args, 1);
4429 cmdlist(n->ncmd.redirect, 0);
4430 break;
4431 case NARG:
4432 p = n->narg.text;
4433 dotail2:
4434 cmdputs(p);
4435 break;
4436 case NHERE:
4437 case NXHERE:
4438 p = "<<...";
4439 goto dotail2;
4440 case NCASE:
4441 cmdputs("case ");
4442 cmdputs(n->ncase.expr->narg.text);
4443 cmdputs(" in ");
4444 for (np = n->ncase.cases; np; np = np->nclist.next) {
4445 cmdtxt(np->nclist.pattern);
4446 cmdputs(") ");
4447 cmdtxt(np->nclist.body);
4448 cmdputs(";; ");
4449 }
4450 p = "esac";
4451 goto dotail2;
4452 case NTO:
4453 p = ">";
4454 goto redir;
4455 case NCLOBBER:
4456 p = ">|";
4457 goto redir;
4458 case NAPPEND:
4459 p = ">>";
4460 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004461#if ENABLE_ASH_BASH_COMPAT
4462 case NTO2:
4463#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004464 case NTOFD:
4465 p = ">&";
4466 goto redir;
4467 case NFROM:
4468 p = "<";
4469 goto redir;
4470 case NFROMFD:
4471 p = "<&";
4472 goto redir;
4473 case NFROMTO:
4474 p = "<>";
4475 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004476 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004477 cmdputs(p);
4478 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004479 cmdputs(utoa(n->ndup.dupfd));
4480 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004481 }
4482 n = n->nfile.fname;
4483 goto donode;
4484 }
4485}
4486
4487static char *
4488commandtext(union node *n)
4489{
4490 char *name;
4491
4492 STARTSTACKSTR(cmdnextc);
4493 cmdtxt(n);
4494 name = stackblock();
4495 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4496 name, cmdnextc, cmdnextc));
4497 return ckstrdup(name);
4498}
4499#endif /* JOBS */
4500
4501/*
4502 * Fork off a subshell. If we are doing job control, give the subshell its
4503 * own process group. Jp is a job structure that the job is to be added to.
4504 * N is the command that will be evaluated by the child. Both jp and n may
4505 * be NULL. The mode parameter can be one of the following:
4506 * FORK_FG - Fork off a foreground process.
4507 * FORK_BG - Fork off a background process.
4508 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4509 * process group even if job control is on.
4510 *
4511 * When job control is turned off, background processes have their standard
4512 * input redirected to /dev/null (except for the second and later processes
4513 * in a pipeline).
4514 *
4515 * Called with interrupts off.
4516 */
4517/*
4518 * Clear traps on a fork.
4519 */
4520static void
4521clear_traps(void)
4522{
4523 char **tp;
4524
4525 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004526 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004527 INT_OFF;
4528 free(*tp);
4529 *tp = NULL;
4530 if (tp != &trap[0])
4531 setsignal(tp - trap);
4532 INT_ON;
4533 }
4534 }
4535}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004536
4537/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004539
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004540/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004541static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004542forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004543{
4544 int oldlvl;
4545
4546 TRACE(("Child shell %d\n", getpid()));
4547 oldlvl = shlvl;
4548 shlvl++;
4549
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004550 /* man bash: "Non-builtin commands run by bash have signal handlers
4551 * set to the values inherited by the shell from its parent".
4552 * Do we do it correctly? */
4553
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004554 closescript();
4555 clear_traps();
4556#if JOBS
4557 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004558 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004559 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4560 pid_t pgrp;
4561
4562 if (jp->nprocs == 0)
4563 pgrp = getpid();
4564 else
4565 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004566 /* this can fail because we are doing it in the parent also */
4567 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568 if (mode == FORK_FG)
4569 xtcsetpgrp(ttyfd, pgrp);
4570 setsignal(SIGTSTP);
4571 setsignal(SIGTTOU);
4572 } else
4573#endif
4574 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004575 /* man bash: "When job control is not in effect,
4576 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004577 ignoresig(SIGINT);
4578 ignoresig(SIGQUIT);
4579 if (jp->nprocs == 0) {
4580 close(0);
4581 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004582 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004583 }
4584 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004585 if (!oldlvl) {
4586 if (iflag) { /* why if iflag only? */
4587 setsignal(SIGINT);
4588 setsignal(SIGTERM);
4589 }
4590 /* man bash:
4591 * "In all cases, bash ignores SIGQUIT. Non-builtin
4592 * commands run by bash have signal handlers
4593 * set to the values inherited by the shell
4594 * from its parent".
4595 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004596 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004597 }
4598 for (jp = curjob; jp; jp = jp->prev_job)
4599 freejob(jp);
4600 jobless = 0;
4601}
4602
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004603/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004604#if !JOBS
4605#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4606#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004607static void
4608forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4609{
4610 TRACE(("In parent shell: child = %d\n", pid));
4611 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004612 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4613 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004614 jobless++;
4615 return;
4616 }
4617#if JOBS
4618 if (mode != FORK_NOJOB && jp->jobctl) {
4619 int pgrp;
4620
4621 if (jp->nprocs == 0)
4622 pgrp = pid;
4623 else
4624 pgrp = jp->ps[0].pid;
4625 /* This can fail because we are doing it in the child also */
4626 setpgid(pid, pgrp);
4627 }
4628#endif
4629 if (mode == FORK_BG) {
4630 backgndpid = pid; /* set $! */
4631 set_curjob(jp, CUR_RUNNING);
4632 }
4633 if (jp) {
4634 struct procstat *ps = &jp->ps[jp->nprocs++];
4635 ps->pid = pid;
4636 ps->status = -1;
4637 ps->cmd = nullstr;
4638#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004639 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004640 ps->cmd = commandtext(n);
4641#endif
4642 }
4643}
4644
4645static int
4646forkshell(struct job *jp, union node *n, int mode)
4647{
4648 int pid;
4649
4650 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4651 pid = fork();
4652 if (pid < 0) {
4653 TRACE(("Fork failed, errno=%d", errno));
4654 if (jp)
4655 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004656 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004657 }
4658 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004659 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004660 else
4661 forkparent(jp, n, mode, pid);
4662 return pid;
4663}
4664
4665/*
4666 * Wait for job to finish.
4667 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004668 * Under job control we have the problem that while a child process
4669 * is running interrupts generated by the user are sent to the child
4670 * but not to the shell. This means that an infinite loop started by
4671 * an interactive user may be hard to kill. With job control turned off,
4672 * an interactive user may place an interactive program inside a loop.
4673 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004674 * these interrupts to also abort the loop. The approach we take here
4675 * is to have the shell ignore interrupt signals while waiting for a
4676 * foreground process to terminate, and then send itself an interrupt
4677 * signal if the child process was terminated by an interrupt signal.
4678 * Unfortunately, some programs want to do a bit of cleanup and then
4679 * exit on interrupt; unless these processes terminate themselves by
4680 * sending a signal to themselves (instead of calling exit) they will
4681 * confuse this approach.
4682 *
4683 * Called with interrupts off.
4684 */
4685static int
4686waitforjob(struct job *jp)
4687{
4688 int st;
4689
4690 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004691
4692 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004693 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004694 /* In non-interactive shells, we _can_ get
4695 * a keyboard signal here and be EINTRed,
4696 * but we just loop back, waiting for command to complete.
4697 *
4698 * man bash:
4699 * "If bash is waiting for a command to complete and receives
4700 * a signal for which a trap has been set, the trap
4701 * will not be executed until the command completes."
4702 *
4703 * Reality is that even if trap is not set, bash
4704 * will not act on the signal until command completes.
4705 * Try this. sleep5intoff.c:
4706 * #include <signal.h>
4707 * #include <unistd.h>
4708 * int main() {
4709 * sigset_t set;
4710 * sigemptyset(&set);
4711 * sigaddset(&set, SIGINT);
4712 * sigaddset(&set, SIGQUIT);
4713 * sigprocmask(SIG_BLOCK, &set, NULL);
4714 * sleep(5);
4715 * return 0;
4716 * }
4717 * $ bash -c './sleep5intoff; echo hi'
4718 * ^C^C^C^C <--- pressing ^C once a second
4719 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004720 * $ bash -c './sleep5intoff; echo hi'
4721 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4722 * $ _
4723 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004724 dowait(DOWAIT_BLOCK, jp);
4725 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004726 INT_ON;
4727
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004728 st = getstatus(jp);
4729#if JOBS
4730 if (jp->jobctl) {
4731 xtcsetpgrp(ttyfd, rootpid);
4732 /*
4733 * This is truly gross.
4734 * If we're doing job control, then we did a TIOCSPGRP which
4735 * caused us (the shell) to no longer be in the controlling
4736 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4737 * intuit from the subprocess exit status whether a SIGINT
4738 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4739 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004740 if (jp->sigint) /* TODO: do the same with all signals */
4741 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004742 }
4743 if (jp->state == JOBDONE)
4744#endif
4745 freejob(jp);
4746 return st;
4747}
4748
4749/*
4750 * return 1 if there are stopped jobs, otherwise 0
4751 */
4752static int
4753stoppedjobs(void)
4754{
4755 struct job *jp;
4756 int retval;
4757
4758 retval = 0;
4759 if (job_warning)
4760 goto out;
4761 jp = curjob;
4762 if (jp && jp->state == JOBSTOPPED) {
4763 out2str("You have stopped jobs.\n");
4764 job_warning = 2;
4765 retval++;
4766 }
4767 out:
4768 return retval;
4769}
4770
4771
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004772/* ============ redir.c
4773 *
4774 * Code for dealing with input/output redirection.
4775 */
4776
4777#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004778#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004779
4780/*
4781 * Open a file in noclobber mode.
4782 * The code was copied from bash.
4783 */
4784static int
4785noclobberopen(const char *fname)
4786{
4787 int r, fd;
4788 struct stat finfo, finfo2;
4789
4790 /*
4791 * If the file exists and is a regular file, return an error
4792 * immediately.
4793 */
4794 r = stat(fname, &finfo);
4795 if (r == 0 && S_ISREG(finfo.st_mode)) {
4796 errno = EEXIST;
4797 return -1;
4798 }
4799
4800 /*
4801 * If the file was not present (r != 0), make sure we open it
4802 * exclusively so that if it is created before we open it, our open
4803 * will fail. Make sure that we do not truncate an existing file.
4804 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4805 * file was not a regular file, we leave O_EXCL off.
4806 */
4807 if (r != 0)
4808 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4809 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4810
4811 /* If the open failed, return the file descriptor right away. */
4812 if (fd < 0)
4813 return fd;
4814
4815 /*
4816 * OK, the open succeeded, but the file may have been changed from a
4817 * non-regular file to a regular file between the stat and the open.
4818 * We are assuming that the O_EXCL open handles the case where FILENAME
4819 * did not exist and is symlinked to an existing file between the stat
4820 * and open.
4821 */
4822
4823 /*
4824 * If we can open it and fstat the file descriptor, and neither check
4825 * revealed that it was a regular file, and the file has not been
4826 * replaced, return the file descriptor.
4827 */
4828 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4829 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4830 return fd;
4831
4832 /* The file has been replaced. badness. */
4833 close(fd);
4834 errno = EEXIST;
4835 return -1;
4836}
4837
4838/*
4839 * Handle here documents. Normally we fork off a process to write the
4840 * data to a pipe. If the document is short, we can stuff the data in
4841 * the pipe without forking.
4842 */
4843/* openhere needs this forward reference */
4844static void expandhere(union node *arg, int fd);
4845static int
4846openhere(union node *redir)
4847{
4848 int pip[2];
4849 size_t len = 0;
4850
4851 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004852 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004853 if (redir->type == NHERE) {
4854 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004855 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004856 full_write(pip[1], redir->nhere.doc->narg.text, len);
4857 goto out;
4858 }
4859 }
4860 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004861 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004862 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004863 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4864 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4865 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4866 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004867 signal(SIGPIPE, SIG_DFL);
4868 if (redir->type == NHERE)
4869 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004870 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004871 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004872 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004873 }
4874 out:
4875 close(pip[1]);
4876 return pip[0];
4877}
4878
4879static int
4880openredirect(union node *redir)
4881{
4882 char *fname;
4883 int f;
4884
4885 switch (redir->nfile.type) {
4886 case NFROM:
4887 fname = redir->nfile.expfname;
4888 f = open(fname, O_RDONLY);
4889 if (f < 0)
4890 goto eopen;
4891 break;
4892 case NFROMTO:
4893 fname = redir->nfile.expfname;
4894 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4895 if (f < 0)
4896 goto ecreate;
4897 break;
4898 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004899#if ENABLE_ASH_BASH_COMPAT
4900 case NTO2:
4901#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004902 /* Take care of noclobber mode. */
4903 if (Cflag) {
4904 fname = redir->nfile.expfname;
4905 f = noclobberopen(fname);
4906 if (f < 0)
4907 goto ecreate;
4908 break;
4909 }
4910 /* FALLTHROUGH */
4911 case NCLOBBER:
4912 fname = redir->nfile.expfname;
4913 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4914 if (f < 0)
4915 goto ecreate;
4916 break;
4917 case NAPPEND:
4918 fname = redir->nfile.expfname;
4919 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4920 if (f < 0)
4921 goto ecreate;
4922 break;
4923 default:
4924#if DEBUG
4925 abort();
4926#endif
4927 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004928/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004929// case NTOFD:
4930// case NFROMFD:
4931// f = -1;
4932// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004933 case NHERE:
4934 case NXHERE:
4935 f = openhere(redir);
4936 break;
4937 }
4938
4939 return f;
4940 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004941 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004942 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004943 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004944}
4945
4946/*
4947 * Copy a file descriptor to be >= to. Returns -1
4948 * if the source file descriptor is closed, EMPTY if there are no unused
4949 * file descriptors left.
4950 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004951/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4952 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004953enum {
4954 COPYFD_EXACT = (int)~(INT_MAX),
4955 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4956};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004957static int
4958copyfd(int from, int to)
4959{
4960 int newfd;
4961
Denis Vlasenko5a867312008-07-24 19:46:38 +00004962 if (to & COPYFD_EXACT) {
4963 to &= ~COPYFD_EXACT;
4964 /*if (from != to)*/
4965 newfd = dup2(from, to);
4966 } else {
4967 newfd = fcntl(from, F_DUPFD, to);
4968 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004969 if (newfd < 0) {
4970 if (errno == EMFILE)
4971 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004972 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004973 ash_msg_and_raise_error("%d: %m", from);
4974 }
4975 return newfd;
4976}
4977
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004978/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004979struct two_fd_t {
4980 int orig, copy;
4981};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004982struct redirtab {
4983 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004984 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004985 int pair_count;
4986 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004987};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004988#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004989
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004990static int need_to_remember(struct redirtab *rp, int fd)
4991{
4992 int i;
4993
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00004994 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004995 return 0;
4996
4997 for (i = 0; i < rp->pair_count; i++) {
4998 if (rp->two_fd[i].orig == fd) {
4999 /* already remembered */
5000 return 0;
5001 }
5002 }
5003 return 1;
5004}
5005
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005006/* "hidden" fd is a fd used to read scripts, or a copy of such */
5007static int is_hidden_fd(struct redirtab *rp, int fd)
5008{
5009 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005010 struct parsefile *pf;
5011
5012 if (fd == -1)
5013 return 0;
5014 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005015 while (pf) {
5016 if (fd == pf->fd) {
5017 return 1;
5018 }
5019 pf = pf->prev;
5020 }
5021 if (!rp)
5022 return 0;
5023 fd |= COPYFD_RESTORE;
5024 for (i = 0; i < rp->pair_count; i++) {
5025 if (rp->two_fd[i].copy == fd) {
5026 return 1;
5027 }
5028 }
5029 return 0;
5030}
5031
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005032/*
5033 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5034 * old file descriptors are stashed away so that the redirection can be
5035 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
5036 * standard output, and the standard error if it becomes a duplicate of
5037 * stdout, is saved in memory.
5038 */
5039/* flags passed to redirect */
5040#define REDIR_PUSH 01 /* save previous values of file descriptors */
5041#define REDIR_SAVEFD2 03 /* set preverrout */
5042static void
5043redirect(union node *redir, int flags)
5044{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005046 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005047 int i;
5048 int fd;
5049 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005050 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005051
Denis Vlasenko01631112007-12-16 17:20:38 +00005052 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005053 if (!redir) {
5054 return;
5055 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005056
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005057 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005058 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005059 INT_OFF;
5060 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005061 union node *tmp = redir;
5062 do {
5063 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005064#if ENABLE_ASH_BASH_COMPAT
5065 if (redir->nfile.type == NTO2)
5066 sv_pos++;
5067#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005068 tmp = tmp->nfile.next;
5069 } while (tmp);
5070 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005071 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005072 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005073 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005074 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005075 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005076 while (sv_pos > 0) {
5077 sv_pos--;
5078 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5079 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005080 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005081
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005082 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005083 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005084 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005085 int right_fd = redir->ndup.dupfd;
5086 /* redirect from/to same file descriptor? */
5087 if (right_fd == fd)
5088 continue;
5089 /* echo >&10 and 10 is a fd opened to the sh script? */
5090 if (is_hidden_fd(sv, right_fd)) {
5091 errno = EBADF; /* as if it is closed */
5092 ash_msg_and_raise_error("%d: %m", right_fd);
5093 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005094 newfd = -1;
5095 } else {
5096 newfd = openredirect(redir); /* always >= 0 */
5097 if (fd == newfd) {
5098 /* Descriptor wasn't open before redirect.
5099 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005100 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005101 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005102 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005103 continue;
5104 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005105 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005106#if ENABLE_ASH_BASH_COMPAT
5107 redirect_more:
5108#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005109 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005110 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005111 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005112/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5113 * are closed in popredir() in the child, preventing them from leaking
5114 * into child. (popredir() also cleans up the mess in case of failures)
5115 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005116 if (i == -1) {
5117 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005118 if (i != EBADF) {
5119 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005120 if (newfd >= 0)
5121 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122 errno = i;
5123 ash_msg_and_raise_error("%d: %m", fd);
5124 /* NOTREACHED */
5125 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005126 /* EBADF: it is not open - good, remember to close it */
5127 remember_to_close:
5128 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005129 } else { /* fd is open, save its copy */
5130 /* "exec fd>&-" should not close fds
5131 * which point to script file(s).
5132 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005133 if (is_hidden_fd(sv, fd))
5134 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005135 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005136 if (fd == 2)
5137 copied_fd2 = i;
5138 sv->two_fd[sv_pos].orig = fd;
5139 sv->two_fd[sv_pos].copy = i;
5140 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005141 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005142 if (newfd < 0) {
5143 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005144 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005145 close(fd);
5146 } else {
5147 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005148 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005149 } else if (fd != newfd) { /* move newfd to fd */
5150 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005151#if ENABLE_ASH_BASH_COMPAT
5152 if (!(redir->nfile.type == NTO2 && fd == 2))
5153#endif
5154 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005155 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005156#if ENABLE_ASH_BASH_COMPAT
5157 if (redir->nfile.type == NTO2 && fd == 1) {
5158 /* We already redirected it to fd 1, now copy it to 2 */
5159 newfd = 1;
5160 fd = 2;
5161 goto redirect_more;
5162 }
5163#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005164 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005165
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005166 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005167 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5168 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005169}
5170
5171/*
5172 * Undo the effects of the last redirection.
5173 */
5174static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005175popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005176{
5177 struct redirtab *rp;
5178 int i;
5179
Denis Vlasenko01631112007-12-16 17:20:38 +00005180 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005181 return;
5182 INT_OFF;
5183 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005184 for (i = 0; i < rp->pair_count; i++) {
5185 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005186 int copy = rp->two_fd[i].copy;
5187 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005188 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005189 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005190 continue;
5191 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005192 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005193 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005194 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005195 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005196 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005197 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005198 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005199 }
5200 }
5201 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005202 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005203 free(rp);
5204 INT_ON;
5205}
5206
5207/*
5208 * Undo all redirections. Called on error or interrupt.
5209 */
5210
5211/*
5212 * Discard all saved file descriptors.
5213 */
5214static void
5215clearredir(int drop)
5216{
5217 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005218 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005219 if (!redirlist)
5220 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005221 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005222 }
5223}
5224
5225static int
5226redirectsafe(union node *redir, int flags)
5227{
5228 int err;
5229 volatile int saveint;
5230 struct jmploc *volatile savehandler = exception_handler;
5231 struct jmploc jmploc;
5232
5233 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005234 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5235 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005236 if (!err) {
5237 exception_handler = &jmploc;
5238 redirect(redir, flags);
5239 }
5240 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005241 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005242 longjmp(exception_handler->loc, 1);
5243 RESTORE_INT(saveint);
5244 return err;
5245}
5246
5247
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005248/* ============ Routines to expand arguments to commands
5249 *
5250 * We have to deal with backquotes, shell variables, and file metacharacters.
5251 */
5252
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005253#if ENABLE_ASH_MATH_SUPPORT_64
5254typedef int64_t arith_t;
5255#define arith_t_type long long
5256#else
5257typedef long arith_t;
5258#define arith_t_type long
5259#endif
5260
5261#if ENABLE_ASH_MATH_SUPPORT
5262static arith_t dash_arith(const char *);
5263static arith_t arith(const char *expr, int *perrcode);
5264#endif
5265
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005266/*
5267 * expandarg flags
5268 */
5269#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5270#define EXP_TILDE 0x2 /* do normal tilde expansion */
5271#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5272#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5273#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5274#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5275#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5276#define EXP_WORD 0x80 /* expand word in parameter expansion */
5277#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5278/*
5279 * _rmescape() flags
5280 */
5281#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5282#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5283#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5284#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5285#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5286
5287/*
5288 * Structure specifying which parts of the string should be searched
5289 * for IFS characters.
5290 */
5291struct ifsregion {
5292 struct ifsregion *next; /* next region in list */
5293 int begoff; /* offset of start of region */
5294 int endoff; /* offset of end of region */
5295 int nulonly; /* search for nul bytes only */
5296};
5297
5298struct arglist {
5299 struct strlist *list;
5300 struct strlist **lastp;
5301};
5302
5303/* output of current string */
5304static char *expdest;
5305/* list of back quote expressions */
5306static struct nodelist *argbackq;
5307/* first struct in list of ifs regions */
5308static struct ifsregion ifsfirst;
5309/* last struct in list */
5310static struct ifsregion *ifslastp;
5311/* holds expanded arg list */
5312static struct arglist exparg;
5313
5314/*
5315 * Our own itoa().
5316 */
5317static int
5318cvtnum(arith_t num)
5319{
5320 int len;
5321
5322 expdest = makestrspace(32, expdest);
5323#if ENABLE_ASH_MATH_SUPPORT_64
5324 len = fmtstr(expdest, 32, "%lld", (long long) num);
5325#else
5326 len = fmtstr(expdest, 32, "%ld", num);
5327#endif
5328 STADJUST(len, expdest);
5329 return len;
5330}
5331
5332static size_t
5333esclen(const char *start, const char *p)
5334{
5335 size_t esc = 0;
5336
5337 while (p > start && *--p == CTLESC) {
5338 esc++;
5339 }
5340 return esc;
5341}
5342
5343/*
5344 * Remove any CTLESC characters from a string.
5345 */
5346static char *
5347_rmescapes(char *str, int flag)
5348{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005349 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005350
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005351 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005352 unsigned inquotes;
5353 int notescaped;
5354 int globbing;
5355
5356 p = strpbrk(str, qchars);
5357 if (!p) {
5358 return str;
5359 }
5360 q = p;
5361 r = str;
5362 if (flag & RMESCAPE_ALLOC) {
5363 size_t len = p - str;
5364 size_t fulllen = len + strlen(p) + 1;
5365
5366 if (flag & RMESCAPE_GROW) {
5367 r = makestrspace(fulllen, expdest);
5368 } else if (flag & RMESCAPE_HEAP) {
5369 r = ckmalloc(fulllen);
5370 } else {
5371 r = stalloc(fulllen);
5372 }
5373 q = r;
5374 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005375 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005376 }
5377 }
5378 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5379 globbing = flag & RMESCAPE_GLOB;
5380 notescaped = globbing;
5381 while (*p) {
5382 if (*p == CTLQUOTEMARK) {
5383 inquotes = ~inquotes;
5384 p++;
5385 notescaped = globbing;
5386 continue;
5387 }
5388 if (*p == '\\') {
5389 /* naked back slash */
5390 notescaped = 0;
5391 goto copy;
5392 }
5393 if (*p == CTLESC) {
5394 p++;
5395 if (notescaped && inquotes && *p != '/') {
5396 *q++ = '\\';
5397 }
5398 }
5399 notescaped = globbing;
5400 copy:
5401 *q++ = *p++;
5402 }
5403 *q = '\0';
5404 if (flag & RMESCAPE_GROW) {
5405 expdest = r;
5406 STADJUST(q - r + 1, expdest);
5407 }
5408 return r;
5409}
5410#define rmescapes(p) _rmescapes((p), 0)
5411
5412#define pmatch(a, b) !fnmatch((a), (b), 0)
5413
5414/*
5415 * Prepare a pattern for a expmeta (internal glob(3)) call.
5416 *
5417 * Returns an stalloced string.
5418 */
5419static char *
5420preglob(const char *pattern, int quoted, int flag)
5421{
5422 flag |= RMESCAPE_GLOB;
5423 if (quoted) {
5424 flag |= RMESCAPE_QUOTED;
5425 }
5426 return _rmescapes((char *)pattern, flag);
5427}
5428
5429/*
5430 * Put a string on the stack.
5431 */
5432static void
5433memtodest(const char *p, size_t len, int syntax, int quotes)
5434{
5435 char *q = expdest;
5436
5437 q = makestrspace(len * 2, q);
5438
5439 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005440 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005441 if (!c)
5442 continue;
5443 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5444 USTPUTC(CTLESC, q);
5445 USTPUTC(c, q);
5446 }
5447
5448 expdest = q;
5449}
5450
5451static void
5452strtodest(const char *p, int syntax, int quotes)
5453{
5454 memtodest(p, strlen(p), syntax, quotes);
5455}
5456
5457/*
5458 * Record the fact that we have to scan this region of the
5459 * string for IFS characters.
5460 */
5461static void
5462recordregion(int start, int end, int nulonly)
5463{
5464 struct ifsregion *ifsp;
5465
5466 if (ifslastp == NULL) {
5467 ifsp = &ifsfirst;
5468 } else {
5469 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005470 ifsp = ckzalloc(sizeof(*ifsp));
5471 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005472 ifslastp->next = ifsp;
5473 INT_ON;
5474 }
5475 ifslastp = ifsp;
5476 ifslastp->begoff = start;
5477 ifslastp->endoff = end;
5478 ifslastp->nulonly = nulonly;
5479}
5480
5481static void
5482removerecordregions(int endoff)
5483{
5484 if (ifslastp == NULL)
5485 return;
5486
5487 if (ifsfirst.endoff > endoff) {
5488 while (ifsfirst.next != NULL) {
5489 struct ifsregion *ifsp;
5490 INT_OFF;
5491 ifsp = ifsfirst.next->next;
5492 free(ifsfirst.next);
5493 ifsfirst.next = ifsp;
5494 INT_ON;
5495 }
5496 if (ifsfirst.begoff > endoff)
5497 ifslastp = NULL;
5498 else {
5499 ifslastp = &ifsfirst;
5500 ifsfirst.endoff = endoff;
5501 }
5502 return;
5503 }
5504
5505 ifslastp = &ifsfirst;
5506 while (ifslastp->next && ifslastp->next->begoff < endoff)
5507 ifslastp=ifslastp->next;
5508 while (ifslastp->next != NULL) {
5509 struct ifsregion *ifsp;
5510 INT_OFF;
5511 ifsp = ifslastp->next->next;
5512 free(ifslastp->next);
5513 ifslastp->next = ifsp;
5514 INT_ON;
5515 }
5516 if (ifslastp->endoff > endoff)
5517 ifslastp->endoff = endoff;
5518}
5519
5520static char *
5521exptilde(char *startp, char *p, int flag)
5522{
5523 char c;
5524 char *name;
5525 struct passwd *pw;
5526 const char *home;
5527 int quotes = flag & (EXP_FULL | EXP_CASE);
5528 int startloc;
5529
5530 name = p + 1;
5531
5532 while ((c = *++p) != '\0') {
5533 switch (c) {
5534 case CTLESC:
5535 return startp;
5536 case CTLQUOTEMARK:
5537 return startp;
5538 case ':':
5539 if (flag & EXP_VARTILDE)
5540 goto done;
5541 break;
5542 case '/':
5543 case CTLENDVAR:
5544 goto done;
5545 }
5546 }
5547 done:
5548 *p = '\0';
5549 if (*name == '\0') {
5550 home = lookupvar(homestr);
5551 } else {
5552 pw = getpwnam(name);
5553 if (pw == NULL)
5554 goto lose;
5555 home = pw->pw_dir;
5556 }
5557 if (!home || !*home)
5558 goto lose;
5559 *p = c;
5560 startloc = expdest - (char *)stackblock();
5561 strtodest(home, SQSYNTAX, quotes);
5562 recordregion(startloc, expdest - (char *)stackblock(), 0);
5563 return p;
5564 lose:
5565 *p = c;
5566 return startp;
5567}
5568
5569/*
5570 * Execute a command inside back quotes. If it's a builtin command, we
5571 * want to save its output in a block obtained from malloc. Otherwise
5572 * we fork off a subprocess and get the output of the command via a pipe.
5573 * Should be called with interrupts off.
5574 */
5575struct backcmd { /* result of evalbackcmd */
5576 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005577 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005578 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005579 struct job *jp; /* job structure for command */
5580};
5581
5582/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005583static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005584#define EV_EXIT 01 /* exit after evaluating tree */
5585static void evaltree(union node *, int);
5586
5587static void
5588evalbackcmd(union node *n, struct backcmd *result)
5589{
5590 int saveherefd;
5591
5592 result->fd = -1;
5593 result->buf = NULL;
5594 result->nleft = 0;
5595 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005596 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005597 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005598
5599 saveherefd = herefd;
5600 herefd = -1;
5601
5602 {
5603 int pip[2];
5604 struct job *jp;
5605
5606 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005607 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005608 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5610 FORCE_INT_ON;
5611 close(pip[0]);
5612 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005613 /*close(1);*/
5614 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005615 close(pip[1]);
5616 }
5617 eflag = 0;
5618 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5619 /* NOTREACHED */
5620 }
5621 close(pip[1]);
5622 result->fd = pip[0];
5623 result->jp = jp;
5624 }
5625 herefd = saveherefd;
5626 out:
5627 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5628 result->fd, result->buf, result->nleft, result->jp));
5629}
5630
5631/*
5632 * Expand stuff in backwards quotes.
5633 */
5634static void
5635expbackq(union node *cmd, int quoted, int quotes)
5636{
5637 struct backcmd in;
5638 int i;
5639 char buf[128];
5640 char *p;
5641 char *dest;
5642 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005643 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644 struct stackmark smark;
5645
5646 INT_OFF;
5647 setstackmark(&smark);
5648 dest = expdest;
5649 startloc = dest - (char *)stackblock();
5650 grabstackstr(dest);
5651 evalbackcmd(cmd, &in);
5652 popstackmark(&smark);
5653
5654 p = in.buf;
5655 i = in.nleft;
5656 if (i == 0)
5657 goto read;
5658 for (;;) {
5659 memtodest(p, i, syntax, quotes);
5660 read:
5661 if (in.fd < 0)
5662 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005663 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005664 TRACE(("expbackq: read returns %d\n", i));
5665 if (i <= 0)
5666 break;
5667 p = buf;
5668 }
5669
Denis Vlasenko60818682007-09-28 22:07:23 +00005670 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005671 if (in.fd >= 0) {
5672 close(in.fd);
5673 back_exitstatus = waitforjob(in.jp);
5674 }
5675 INT_ON;
5676
5677 /* Eat all trailing newlines */
5678 dest = expdest;
5679 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5680 STUNPUTC(dest);
5681 expdest = dest;
5682
5683 if (quoted == 0)
5684 recordregion(startloc, dest - (char *)stackblock(), 0);
5685 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5686 (dest - (char *)stackblock()) - startloc,
5687 (dest - (char *)stackblock()) - startloc,
5688 stackblock() + startloc));
5689}
5690
5691#if ENABLE_ASH_MATH_SUPPORT
5692/*
5693 * Expand arithmetic expression. Backup to start of expression,
5694 * evaluate, place result in (backed up) result, adjust string position.
5695 */
5696static void
5697expari(int quotes)
5698{
5699 char *p, *start;
5700 int begoff;
5701 int flag;
5702 int len;
5703
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005704 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005705
5706 /*
5707 * This routine is slightly over-complicated for
5708 * efficiency. Next we scan backwards looking for the
5709 * start of arithmetic.
5710 */
5711 start = stackblock();
5712 p = expdest - 1;
5713 *p = '\0';
5714 p--;
5715 do {
5716 int esc;
5717
5718 while (*p != CTLARI) {
5719 p--;
5720#if DEBUG
5721 if (p < start) {
5722 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5723 }
5724#endif
5725 }
5726
5727 esc = esclen(start, p);
5728 if (!(esc % 2)) {
5729 break;
5730 }
5731
5732 p -= esc + 1;
5733 } while (1);
5734
5735 begoff = p - start;
5736
5737 removerecordregions(begoff);
5738
5739 flag = p[1];
5740
5741 expdest = p;
5742
5743 if (quotes)
5744 rmescapes(p + 2);
5745
5746 len = cvtnum(dash_arith(p + 2));
5747
5748 if (flag != '"')
5749 recordregion(begoff, begoff + len, 0);
5750}
5751#endif
5752
5753/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005754static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005755
5756/*
5757 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5758 * characters to allow for further processing. Otherwise treat
5759 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005760 *
5761 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5762 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5763 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005764 */
5765static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005766argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005767{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005768 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005769 '=',
5770 ':',
5771 CTLQUOTEMARK,
5772 CTLENDVAR,
5773 CTLESC,
5774 CTLVAR,
5775 CTLBACKQ,
5776 CTLBACKQ | CTLQUOTE,
5777#if ENABLE_ASH_MATH_SUPPORT
5778 CTLENDARI,
5779#endif
5780 0
5781 };
5782 const char *reject = spclchars;
5783 int c;
5784 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5785 int breakall = flag & EXP_WORD;
5786 int inquotes;
5787 size_t length;
5788 int startloc;
5789
5790 if (!(flag & EXP_VARTILDE)) {
5791 reject += 2;
5792 } else if (flag & EXP_VARTILDE2) {
5793 reject++;
5794 }
5795 inquotes = 0;
5796 length = 0;
5797 if (flag & EXP_TILDE) {
5798 char *q;
5799
5800 flag &= ~EXP_TILDE;
5801 tilde:
5802 q = p;
5803 if (*q == CTLESC && (flag & EXP_QWORD))
5804 q++;
5805 if (*q == '~')
5806 p = exptilde(p, q, flag);
5807 }
5808 start:
5809 startloc = expdest - (char *)stackblock();
5810 for (;;) {
5811 length += strcspn(p + length, reject);
5812 c = p[length];
5813 if (c && (!(c & 0x80)
5814#if ENABLE_ASH_MATH_SUPPORT
5815 || c == CTLENDARI
5816#endif
5817 )) {
5818 /* c == '=' || c == ':' || c == CTLENDARI */
5819 length++;
5820 }
5821 if (length > 0) {
5822 int newloc;
5823 expdest = stack_nputstr(p, length, expdest);
5824 newloc = expdest - (char *)stackblock();
5825 if (breakall && !inquotes && newloc > startloc) {
5826 recordregion(startloc, newloc, 0);
5827 }
5828 startloc = newloc;
5829 }
5830 p += length + 1;
5831 length = 0;
5832
5833 switch (c) {
5834 case '\0':
5835 goto breakloop;
5836 case '=':
5837 if (flag & EXP_VARTILDE2) {
5838 p--;
5839 continue;
5840 }
5841 flag |= EXP_VARTILDE2;
5842 reject++;
5843 /* fall through */
5844 case ':':
5845 /*
5846 * sort of a hack - expand tildes in variable
5847 * assignments (after the first '=' and after ':'s).
5848 */
5849 if (*--p == '~') {
5850 goto tilde;
5851 }
5852 continue;
5853 }
5854
5855 switch (c) {
5856 case CTLENDVAR: /* ??? */
5857 goto breakloop;
5858 case CTLQUOTEMARK:
5859 /* "$@" syntax adherence hack */
5860 if (
5861 !inquotes &&
5862 !memcmp(p, dolatstr, 4) &&
5863 (p[4] == CTLQUOTEMARK || (
5864 p[4] == CTLENDVAR &&
5865 p[5] == CTLQUOTEMARK
5866 ))
5867 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005868 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005869 goto start;
5870 }
5871 inquotes = !inquotes;
5872 addquote:
5873 if (quotes) {
5874 p--;
5875 length++;
5876 startloc++;
5877 }
5878 break;
5879 case CTLESC:
5880 startloc++;
5881 length++;
5882 goto addquote;
5883 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005884 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005885 goto start;
5886 case CTLBACKQ:
5887 c = 0;
5888 case CTLBACKQ|CTLQUOTE:
5889 expbackq(argbackq->n, c, quotes);
5890 argbackq = argbackq->next;
5891 goto start;
5892#if ENABLE_ASH_MATH_SUPPORT
5893 case CTLENDARI:
5894 p--;
5895 expari(quotes);
5896 goto start;
5897#endif
5898 }
5899 }
5900 breakloop:
5901 ;
5902}
5903
5904static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005905scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005906 int zero)
5907{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005908// This commented out code was added by James Simmons <jsimmons@infradead.org>
5909// as part of a larger change when he added support for ${var/a/b}.
5910// However, it broke # and % operators:
5911//
5912//var=ababcdcd
5913// ok bad
5914//echo ${var#ab} abcdcd abcdcd
5915//echo ${var##ab} abcdcd abcdcd
5916//echo ${var#a*b} abcdcd ababcdcd (!)
5917//echo ${var##a*b} cdcd cdcd
5918//echo ${var#?} babcdcd ababcdcd (!)
5919//echo ${var##?} babcdcd babcdcd
5920//echo ${var#*} ababcdcd babcdcd (!)
5921//echo ${var##*}
5922//echo ${var%cd} ababcd ababcd
5923//echo ${var%%cd} ababcd abab (!)
5924//echo ${var%c*d} ababcd ababcd
5925//echo ${var%%c*d} abab ababcdcd (!)
5926//echo ${var%?} ababcdc ababcdc
5927//echo ${var%%?} ababcdc ababcdcd (!)
5928//echo ${var%*} ababcdcd ababcdcd
5929//echo ${var%%*}
5930//
5931// Commenting it back out helped. Remove it completely if it really
5932// is not needed.
5933
5934 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005935 char c;
5936
5937 loc = startp;
5938 loc2 = rmesc;
5939 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005940 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005941 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005942
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005943 c = *loc2;
5944 if (zero) {
5945 *loc2 = '\0';
5946 s = rmesc;
5947 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005948 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005949
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005950// // chop off end if its '*'
5951// full = strrchr(str, '*');
5952// if (full && full != str)
5953// match--;
5954//
5955// // If str starts with '*' replace with s.
5956// if ((*str == '*') && strlen(s) >= match) {
5957// full = xstrdup(s);
5958// strncpy(full+strlen(s)-match+1, str+1, match-1);
5959// } else
5960// full = xstrndup(str, match);
5961// match = strncmp(s, full, strlen(full));
5962// free(full);
5963//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005964 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005965 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005966 return loc;
5967 if (quotes && *loc == CTLESC)
5968 loc++;
5969 loc++;
5970 loc2++;
5971 } while (c);
5972 return 0;
5973}
5974
5975static char *
5976scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5977 int zero)
5978{
5979 int esc = 0;
5980 char *loc;
5981 char *loc2;
5982
5983 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5984 int match;
5985 char c = *loc2;
5986 const char *s = loc2;
5987 if (zero) {
5988 *loc2 = '\0';
5989 s = rmesc;
5990 }
5991 match = pmatch(str, s);
5992 *loc2 = c;
5993 if (match)
5994 return loc;
5995 loc--;
5996 if (quotes) {
5997 if (--esc < 0) {
5998 esc = esclen(startp, loc);
5999 }
6000 if (esc % 2) {
6001 esc--;
6002 loc--;
6003 }
6004 }
6005 }
6006 return 0;
6007}
6008
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006009static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006010static void
6011varunset(const char *end, const char *var, const char *umsg, int varflags)
6012{
6013 const char *msg;
6014 const char *tail;
6015
6016 tail = nullstr;
6017 msg = "parameter not set";
6018 if (umsg) {
6019 if (*end == CTLENDVAR) {
6020 if (varflags & VSNUL)
6021 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006022 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006024 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025 }
6026 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6027}
6028
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006029#if ENABLE_ASH_BASH_COMPAT
6030static char *
6031parse_sub_pattern(char *arg, int inquotes)
6032{
6033 char *idx, *repl = NULL;
6034 unsigned char c;
6035
Denis Vlasenko2659c632008-06-14 06:04:59 +00006036 idx = arg;
6037 while (1) {
6038 c = *arg;
6039 if (!c)
6040 break;
6041 if (c == '/') {
6042 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006043 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006044 repl = idx + 1;
6045 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006046 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006047 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006048 *idx++ = c;
6049 if (!inquotes && c == '\\' && arg[1] == '\\')
6050 arg++; /* skip both \\, not just first one */
6051 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006052 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006053 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006054
6055 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006056}
6057#endif /* ENABLE_ASH_BASH_COMPAT */
6058
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006059static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006060subevalvar(char *p, char *str, int strloc, int subtype,
6061 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006062{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006063 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006064 char *startp;
6065 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006066 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006067 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6068 USE_ASH_BASH_COMPAT(char null = '\0';)
6069 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6070 int saveherefd = herefd;
6071 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006073 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006074
6075 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006076 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6077 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006078 STPUTC('\0', expdest);
6079 herefd = saveherefd;
6080 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006081 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006082
6083 switch (subtype) {
6084 case VSASSIGN:
6085 setvar(str, startp, 0);
6086 amount = startp - expdest;
6087 STADJUST(amount, expdest);
6088 return startp;
6089
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006090#if ENABLE_ASH_BASH_COMPAT
6091 case VSSUBSTR:
6092 loc = str = stackblock() + strloc;
6093// TODO: number() instead? It does error checking...
6094 pos = atoi(loc);
6095 len = str - startp - 1;
6096
6097 /* *loc != '\0', guaranteed by parser */
6098 if (quotes) {
6099 char *ptr;
6100
6101 /* We must adjust the length by the number of escapes we find. */
6102 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006103 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006104 len--;
6105 ptr++;
6106 }
6107 }
6108 }
6109 orig_len = len;
6110
6111 if (*loc++ == ':') {
6112// TODO: number() instead? It does error checking...
6113 len = atoi(loc);
6114 } else {
6115 len = orig_len;
6116 while (*loc && *loc != ':')
6117 loc++;
6118 if (*loc++ == ':')
6119// TODO: number() instead? It does error checking...
6120 len = atoi(loc);
6121 }
6122 if (pos >= orig_len) {
6123 pos = 0;
6124 len = 0;
6125 }
6126 if (len > (orig_len - pos))
6127 len = orig_len - pos;
6128
6129 for (str = startp; pos; str++, pos--) {
6130 if (quotes && *str == CTLESC)
6131 str++;
6132 }
6133 for (loc = startp; len; len--) {
6134 if (quotes && *str == CTLESC)
6135 *loc++ = *str++;
6136 *loc++ = *str++;
6137 }
6138 *loc = '\0';
6139 amount = loc - expdest;
6140 STADJUST(amount, expdest);
6141 return loc;
6142#endif
6143
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006144 case VSQUESTION:
6145 varunset(p, str, startp, varflags);
6146 /* NOTREACHED */
6147 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006148 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006149
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006150 /* We'll comeback here if we grow the stack while handling
6151 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6152 * stack will need rebasing, and we'll need to remove our work
6153 * areas each time
6154 */
6155 USE_ASH_BASH_COMPAT(restart:)
6156
6157 amount = expdest - ((char *)stackblock() + resetloc);
6158 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006159 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006160
6161 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006162 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006163 if (quotes) {
6164 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6165 if (rmesc != startp) {
6166 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006167 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 }
6169 }
6170 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006171 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006172 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006173 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006174
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006175#if ENABLE_ASH_BASH_COMPAT
6176 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6177 char *idx, *end, *restart_detect;
6178
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006179 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006180 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6181 if (!repl)
6182 repl = &null;
6183 }
6184
6185 /* If there's no pattern to match, return the expansion unmolested */
6186 if (*str == '\0')
6187 return 0;
6188
6189 len = 0;
6190 idx = startp;
6191 end = str - 1;
6192 while (idx < end) {
6193 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6194 if (!loc) {
6195 /* No match, advance */
6196 restart_detect = stackblock();
6197 STPUTC(*idx, expdest);
6198 if (quotes && *idx == CTLESC) {
6199 idx++;
6200 len++;
6201 STPUTC(*idx, expdest);
6202 }
6203 if (stackblock() != restart_detect)
6204 goto restart;
6205 idx++;
6206 len++;
6207 rmesc++;
6208 continue;
6209 }
6210
6211 if (subtype == VSREPLACEALL) {
6212 while (idx < loc) {
6213 if (quotes && *idx == CTLESC)
6214 idx++;
6215 idx++;
6216 rmesc++;
6217 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006218 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006219 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006220 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006221
6222 for (loc = repl; *loc; loc++) {
6223 restart_detect = stackblock();
6224 STPUTC(*loc, expdest);
6225 if (stackblock() != restart_detect)
6226 goto restart;
6227 len++;
6228 }
6229
6230 if (subtype == VSREPLACE) {
6231 while (*idx) {
6232 restart_detect = stackblock();
6233 STPUTC(*idx, expdest);
6234 if (stackblock() != restart_detect)
6235 goto restart;
6236 len++;
6237 idx++;
6238 }
6239 break;
6240 }
6241 }
6242
6243 /* We've put the replaced text into a buffer at workloc, now
6244 * move it to the right place and adjust the stack.
6245 */
6246 startp = stackblock() + startloc;
6247 STPUTC('\0', expdest);
6248 memmove(startp, stackblock() + workloc, len);
6249 startp[len++] = '\0';
6250 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6251 STADJUST(-amount, expdest);
6252 return startp;
6253 }
6254#endif /* ENABLE_ASH_BASH_COMPAT */
6255
6256 subtype -= VSTRIMRIGHT;
6257#if DEBUG
6258 if (subtype < 0 || subtype > 7)
6259 abort();
6260#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006261 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6262 zero = subtype >> 1;
6263 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6264 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6265
6266 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6267 if (loc) {
6268 if (zero) {
6269 memmove(startp, loc, str - loc);
6270 loc = startp + (str - loc) - 1;
6271 }
6272 *loc = '\0';
6273 amount = loc - expdest;
6274 STADJUST(amount, expdest);
6275 }
6276 return loc;
6277}
6278
6279/*
6280 * Add the value of a specialized variable to the stack string.
6281 */
6282static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006283varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006284{
6285 int num;
6286 char *p;
6287 int i;
6288 int sep = 0;
6289 int sepq = 0;
6290 ssize_t len = 0;
6291 char **ap;
6292 int syntax;
6293 int quoted = varflags & VSQUOTE;
6294 int subtype = varflags & VSTYPE;
6295 int quotes = flags & (EXP_FULL | EXP_CASE);
6296
6297 if (quoted && (flags & EXP_FULL))
6298 sep = 1 << CHAR_BIT;
6299
6300 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6301 switch (*name) {
6302 case '$':
6303 num = rootpid;
6304 goto numvar;
6305 case '?':
6306 num = exitstatus;
6307 goto numvar;
6308 case '#':
6309 num = shellparam.nparam;
6310 goto numvar;
6311 case '!':
6312 num = backgndpid;
6313 if (num == 0)
6314 return -1;
6315 numvar:
6316 len = cvtnum(num);
6317 break;
6318 case '-':
6319 p = makestrspace(NOPTS, expdest);
6320 for (i = NOPTS - 1; i >= 0; i--) {
6321 if (optlist[i]) {
6322 USTPUTC(optletters(i), p);
6323 len++;
6324 }
6325 }
6326 expdest = p;
6327 break;
6328 case '@':
6329 if (sep)
6330 goto param;
6331 /* fall through */
6332 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006333 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6335 sepq = 1;
6336 param:
6337 ap = shellparam.p;
6338 if (!ap)
6339 return -1;
6340 while ((p = *ap++)) {
6341 size_t partlen;
6342
6343 partlen = strlen(p);
6344 len += partlen;
6345
6346 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6347 memtodest(p, partlen, syntax, quotes);
6348
6349 if (*ap && sep) {
6350 char *q;
6351
6352 len++;
6353 if (subtype == VSPLUS || subtype == VSLENGTH) {
6354 continue;
6355 }
6356 q = expdest;
6357 if (sepq)
6358 STPUTC(CTLESC, q);
6359 STPUTC(sep, q);
6360 expdest = q;
6361 }
6362 }
6363 return len;
6364 case '0':
6365 case '1':
6366 case '2':
6367 case '3':
6368 case '4':
6369 case '5':
6370 case '6':
6371 case '7':
6372 case '8':
6373 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006374// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006375 num = atoi(name);
6376 if (num < 0 || num > shellparam.nparam)
6377 return -1;
6378 p = num ? shellparam.p[num - 1] : arg0;
6379 goto value;
6380 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006381 /* NB: name has form "VAR=..." */
6382
6383 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6384 * which should be considered before we check variables. */
6385 if (var_str_list) {
6386 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6387 p = NULL;
6388 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006389 char *str, *eq;
6390 str = var_str_list->text;
6391 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006392 if (!eq) /* stop at first non-assignment */
6393 break;
6394 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006395 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006396 && strncmp(str, name, name_len) == 0) {
6397 p = eq;
6398 /* goto value; - WRONG! */
6399 /* think "A=1 A=2 B=$A" */
6400 }
6401 var_str_list = var_str_list->next;
6402 } while (var_str_list);
6403 if (p)
6404 goto value;
6405 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006406 p = lookupvar(name);
6407 value:
6408 if (!p)
6409 return -1;
6410
6411 len = strlen(p);
6412 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6413 memtodest(p, len, syntax, quotes);
6414 return len;
6415 }
6416
6417 if (subtype == VSPLUS || subtype == VSLENGTH)
6418 STADJUST(-len, expdest);
6419 return len;
6420}
6421
6422/*
6423 * Expand a variable, and return a pointer to the next character in the
6424 * input string.
6425 */
6426static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006427evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006428{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006429 char varflags;
6430 char subtype;
6431 char quoted;
6432 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433 char *var;
6434 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006435 int startloc;
6436 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006437
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006438 varflags = *p++;
6439 subtype = varflags & VSTYPE;
6440 quoted = varflags & VSQUOTE;
6441 var = p;
6442 easy = (!quoted || (*var == '@' && shellparam.nparam));
6443 startloc = expdest - (char *)stackblock();
6444 p = strchr(p, '=') + 1;
6445
6446 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006447 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006448 if (varflags & VSNUL)
6449 varlen--;
6450
6451 if (subtype == VSPLUS) {
6452 varlen = -1 - varlen;
6453 goto vsplus;
6454 }
6455
6456 if (subtype == VSMINUS) {
6457 vsplus:
6458 if (varlen < 0) {
6459 argstr(
6460 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006461 (quoted ? EXP_QWORD : EXP_WORD),
6462 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006463 );
6464 goto end;
6465 }
6466 if (easy)
6467 goto record;
6468 goto end;
6469 }
6470
6471 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6472 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006473 if (subevalvar(p, var, /* strloc: */ 0,
6474 subtype, startloc, varflags,
6475 /* quotes: */ 0,
6476 var_str_list)
6477 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006478 varflags &= ~VSNUL;
6479 /*
6480 * Remove any recorded regions beyond
6481 * start of variable
6482 */
6483 removerecordregions(startloc);
6484 goto again;
6485 }
6486 goto end;
6487 }
6488 if (easy)
6489 goto record;
6490 goto end;
6491 }
6492
6493 if (varlen < 0 && uflag)
6494 varunset(p, var, 0, 0);
6495
6496 if (subtype == VSLENGTH) {
6497 cvtnum(varlen > 0 ? varlen : 0);
6498 goto record;
6499 }
6500
6501 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006502 if (easy)
6503 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006504 goto end;
6505 }
6506
6507#if DEBUG
6508 switch (subtype) {
6509 case VSTRIMLEFT:
6510 case VSTRIMLEFTMAX:
6511 case VSTRIMRIGHT:
6512 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006513#if ENABLE_ASH_BASH_COMPAT
6514 case VSSUBSTR:
6515 case VSREPLACE:
6516 case VSREPLACEALL:
6517#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006518 break;
6519 default:
6520 abort();
6521 }
6522#endif
6523
6524 if (varlen >= 0) {
6525 /*
6526 * Terminate the string and start recording the pattern
6527 * right after it
6528 */
6529 STPUTC('\0', expdest);
6530 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006531 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6532 startloc, varflags,
6533 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6534 var_str_list)
6535 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006536 int amount = expdest - (
6537 (char *)stackblock() + patloc - 1
6538 );
6539 STADJUST(-amount, expdest);
6540 }
6541 /* Remove any recorded regions beyond start of variable */
6542 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006543 record:
6544 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006545 }
6546
6547 end:
6548 if (subtype != VSNORMAL) { /* skip to end of alternative */
6549 int nesting = 1;
6550 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006551 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 if (c == CTLESC)
6553 p++;
6554 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6555 if (varlen >= 0)
6556 argbackq = argbackq->next;
6557 } else if (c == CTLVAR) {
6558 if ((*p++ & VSTYPE) != VSNORMAL)
6559 nesting++;
6560 } else if (c == CTLENDVAR) {
6561 if (--nesting == 0)
6562 break;
6563 }
6564 }
6565 }
6566 return p;
6567}
6568
6569/*
6570 * Break the argument string into pieces based upon IFS and add the
6571 * strings to the argument list. The regions of the string to be
6572 * searched for IFS characters have been stored by recordregion.
6573 */
6574static void
6575ifsbreakup(char *string, struct arglist *arglist)
6576{
6577 struct ifsregion *ifsp;
6578 struct strlist *sp;
6579 char *start;
6580 char *p;
6581 char *q;
6582 const char *ifs, *realifs;
6583 int ifsspc;
6584 int nulonly;
6585
6586 start = string;
6587 if (ifslastp != NULL) {
6588 ifsspc = 0;
6589 nulonly = 0;
6590 realifs = ifsset() ? ifsval() : defifs;
6591 ifsp = &ifsfirst;
6592 do {
6593 p = string + ifsp->begoff;
6594 nulonly = ifsp->nulonly;
6595 ifs = nulonly ? nullstr : realifs;
6596 ifsspc = 0;
6597 while (p < string + ifsp->endoff) {
6598 q = p;
6599 if (*p == CTLESC)
6600 p++;
6601 if (!strchr(ifs, *p)) {
6602 p++;
6603 continue;
6604 }
6605 if (!nulonly)
6606 ifsspc = (strchr(defifs, *p) != NULL);
6607 /* Ignore IFS whitespace at start */
6608 if (q == start && ifsspc) {
6609 p++;
6610 start = p;
6611 continue;
6612 }
6613 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006614 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006615 sp->text = start;
6616 *arglist->lastp = sp;
6617 arglist->lastp = &sp->next;
6618 p++;
6619 if (!nulonly) {
6620 for (;;) {
6621 if (p >= string + ifsp->endoff) {
6622 break;
6623 }
6624 q = p;
6625 if (*p == CTLESC)
6626 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006627 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006628 p = q;
6629 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006630 }
6631 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006632 if (ifsspc) {
6633 p++;
6634 ifsspc = 0;
6635 } else {
6636 p = q;
6637 break;
6638 }
6639 } else
6640 p++;
6641 }
6642 }
6643 start = p;
6644 } /* while */
6645 ifsp = ifsp->next;
6646 } while (ifsp != NULL);
6647 if (nulonly)
6648 goto add;
6649 }
6650
6651 if (!*start)
6652 return;
6653
6654 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006655 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006656 sp->text = start;
6657 *arglist->lastp = sp;
6658 arglist->lastp = &sp->next;
6659}
6660
6661static void
6662ifsfree(void)
6663{
6664 struct ifsregion *p;
6665
6666 INT_OFF;
6667 p = ifsfirst.next;
6668 do {
6669 struct ifsregion *ifsp;
6670 ifsp = p->next;
6671 free(p);
6672 p = ifsp;
6673 } while (p);
6674 ifslastp = NULL;
6675 ifsfirst.next = NULL;
6676 INT_ON;
6677}
6678
6679/*
6680 * Add a file name to the list.
6681 */
6682static void
6683addfname(const char *name)
6684{
6685 struct strlist *sp;
6686
Denis Vlasenko597906c2008-02-20 16:38:54 +00006687 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006688 sp->text = ststrdup(name);
6689 *exparg.lastp = sp;
6690 exparg.lastp = &sp->next;
6691}
6692
6693static char *expdir;
6694
6695/*
6696 * Do metacharacter (i.e. *, ?, [...]) expansion.
6697 */
6698static void
6699expmeta(char *enddir, char *name)
6700{
6701 char *p;
6702 const char *cp;
6703 char *start;
6704 char *endname;
6705 int metaflag;
6706 struct stat statb;
6707 DIR *dirp;
6708 struct dirent *dp;
6709 int atend;
6710 int matchdot;
6711
6712 metaflag = 0;
6713 start = name;
6714 for (p = name; *p; p++) {
6715 if (*p == '*' || *p == '?')
6716 metaflag = 1;
6717 else if (*p == '[') {
6718 char *q = p + 1;
6719 if (*q == '!')
6720 q++;
6721 for (;;) {
6722 if (*q == '\\')
6723 q++;
6724 if (*q == '/' || *q == '\0')
6725 break;
6726 if (*++q == ']') {
6727 metaflag = 1;
6728 break;
6729 }
6730 }
6731 } else if (*p == '\\')
6732 p++;
6733 else if (*p == '/') {
6734 if (metaflag)
6735 goto out;
6736 start = p + 1;
6737 }
6738 }
6739 out:
6740 if (metaflag == 0) { /* we've reached the end of the file name */
6741 if (enddir != expdir)
6742 metaflag++;
6743 p = name;
6744 do {
6745 if (*p == '\\')
6746 p++;
6747 *enddir++ = *p;
6748 } while (*p++);
6749 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6750 addfname(expdir);
6751 return;
6752 }
6753 endname = p;
6754 if (name < start) {
6755 p = name;
6756 do {
6757 if (*p == '\\')
6758 p++;
6759 *enddir++ = *p++;
6760 } while (p < start);
6761 }
6762 if (enddir == expdir) {
6763 cp = ".";
6764 } else if (enddir == expdir + 1 && *expdir == '/') {
6765 cp = "/";
6766 } else {
6767 cp = expdir;
6768 enddir[-1] = '\0';
6769 }
6770 dirp = opendir(cp);
6771 if (dirp == NULL)
6772 return;
6773 if (enddir != expdir)
6774 enddir[-1] = '/';
6775 if (*endname == 0) {
6776 atend = 1;
6777 } else {
6778 atend = 0;
6779 *endname++ = '\0';
6780 }
6781 matchdot = 0;
6782 p = start;
6783 if (*p == '\\')
6784 p++;
6785 if (*p == '.')
6786 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006787 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006788 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 continue;
6790 if (pmatch(start, dp->d_name)) {
6791 if (atend) {
6792 strcpy(enddir, dp->d_name);
6793 addfname(expdir);
6794 } else {
6795 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6796 continue;
6797 p[-1] = '/';
6798 expmeta(p, endname);
6799 }
6800 }
6801 }
6802 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006803 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006804 endname[-1] = '/';
6805}
6806
6807static struct strlist *
6808msort(struct strlist *list, int len)
6809{
6810 struct strlist *p, *q = NULL;
6811 struct strlist **lpp;
6812 int half;
6813 int n;
6814
6815 if (len <= 1)
6816 return list;
6817 half = len >> 1;
6818 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006819 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006820 q = p;
6821 p = p->next;
6822 }
6823 q->next = NULL; /* terminate first half of list */
6824 q = msort(list, half); /* sort first half of list */
6825 p = msort(p, len - half); /* sort second half */
6826 lpp = &list;
6827 for (;;) {
6828#if ENABLE_LOCALE_SUPPORT
6829 if (strcoll(p->text, q->text) < 0)
6830#else
6831 if (strcmp(p->text, q->text) < 0)
6832#endif
6833 {
6834 *lpp = p;
6835 lpp = &p->next;
6836 p = *lpp;
6837 if (p == NULL) {
6838 *lpp = q;
6839 break;
6840 }
6841 } else {
6842 *lpp = q;
6843 lpp = &q->next;
6844 q = *lpp;
6845 if (q == NULL) {
6846 *lpp = p;
6847 break;
6848 }
6849 }
6850 }
6851 return list;
6852}
6853
6854/*
6855 * Sort the results of file name expansion. It calculates the number of
6856 * strings to sort and then calls msort (short for merge sort) to do the
6857 * work.
6858 */
6859static struct strlist *
6860expsort(struct strlist *str)
6861{
6862 int len;
6863 struct strlist *sp;
6864
6865 len = 0;
6866 for (sp = str; sp; sp = sp->next)
6867 len++;
6868 return msort(str, len);
6869}
6870
6871static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006872expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006873{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006874 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006875 '*', '?', '[', 0
6876 };
6877 /* TODO - EXP_REDIR */
6878
6879 while (str) {
6880 struct strlist **savelastp;
6881 struct strlist *sp;
6882 char *p;
6883
6884 if (fflag)
6885 goto nometa;
6886 if (!strpbrk(str->text, metachars))
6887 goto nometa;
6888 savelastp = exparg.lastp;
6889
6890 INT_OFF;
6891 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6892 {
6893 int i = strlen(str->text);
6894 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6895 }
6896
6897 expmeta(expdir, p);
6898 free(expdir);
6899 if (p != str->text)
6900 free(p);
6901 INT_ON;
6902 if (exparg.lastp == savelastp) {
6903 /*
6904 * no matches
6905 */
6906 nometa:
6907 *exparg.lastp = str;
6908 rmescapes(str->text);
6909 exparg.lastp = &str->next;
6910 } else {
6911 *exparg.lastp = NULL;
6912 *savelastp = sp = expsort(*savelastp);
6913 while (sp->next != NULL)
6914 sp = sp->next;
6915 exparg.lastp = &sp->next;
6916 }
6917 str = str->next;
6918 }
6919}
6920
6921/*
6922 * Perform variable substitution and command substitution on an argument,
6923 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6924 * perform splitting and file name expansion. When arglist is NULL, perform
6925 * here document expansion.
6926 */
6927static void
6928expandarg(union node *arg, struct arglist *arglist, int flag)
6929{
6930 struct strlist *sp;
6931 char *p;
6932
6933 argbackq = arg->narg.backquote;
6934 STARTSTACKSTR(expdest);
6935 ifsfirst.next = NULL;
6936 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006937 argstr(arg->narg.text, flag,
6938 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006939 p = _STPUTC('\0', expdest);
6940 expdest = p - 1;
6941 if (arglist == NULL) {
6942 return; /* here document expanded */
6943 }
6944 p = grabstackstr(p);
6945 exparg.lastp = &exparg.list;
6946 /*
6947 * TODO - EXP_REDIR
6948 */
6949 if (flag & EXP_FULL) {
6950 ifsbreakup(p, &exparg);
6951 *exparg.lastp = NULL;
6952 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006953 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006954 } else {
6955 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6956 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006957 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006958 sp->text = p;
6959 *exparg.lastp = sp;
6960 exparg.lastp = &sp->next;
6961 }
6962 if (ifsfirst.next)
6963 ifsfree();
6964 *exparg.lastp = NULL;
6965 if (exparg.list) {
6966 *arglist->lastp = exparg.list;
6967 arglist->lastp = exparg.lastp;
6968 }
6969}
6970
6971/*
6972 * Expand shell variables and backquotes inside a here document.
6973 */
6974static void
6975expandhere(union node *arg, int fd)
6976{
6977 herefd = fd;
6978 expandarg(arg, (struct arglist *)NULL, 0);
6979 full_write(fd, stackblock(), expdest - (char *)stackblock());
6980}
6981
6982/*
6983 * Returns true if the pattern matches the string.
6984 */
6985static int
6986patmatch(char *pattern, const char *string)
6987{
6988 return pmatch(preglob(pattern, 0, 0), string);
6989}
6990
6991/*
6992 * See if a pattern matches in a case statement.
6993 */
6994static int
6995casematch(union node *pattern, char *val)
6996{
6997 struct stackmark smark;
6998 int result;
6999
7000 setstackmark(&smark);
7001 argbackq = pattern->narg.backquote;
7002 STARTSTACKSTR(expdest);
7003 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007004 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7005 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007006 STACKSTRNUL(expdest);
7007 result = patmatch(stackblock(), val);
7008 popstackmark(&smark);
7009 return result;
7010}
7011
7012
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007013/* ============ find_command */
7014
7015struct builtincmd {
7016 const char *name;
7017 int (*builtin)(int, char **);
7018 /* unsigned flags; */
7019};
7020#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007021/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007022 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007023#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007024#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007025
7026struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007027 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007028 union param {
7029 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007030 /* index >= 0 for commands without path (slashes) */
7031 /* (TODO: what exactly does the value mean? PATH position?) */
7032 /* index == -1 for commands with slashes */
7033 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007034 const struct builtincmd *cmd;
7035 struct funcnode *func;
7036 } u;
7037};
7038/* values of cmdtype */
7039#define CMDUNKNOWN -1 /* no entry in table for command */
7040#define CMDNORMAL 0 /* command is an executable program */
7041#define CMDFUNCTION 1 /* command is a shell function */
7042#define CMDBUILTIN 2 /* command is a shell builtin */
7043
7044/* action to find_command() */
7045#define DO_ERR 0x01 /* prints errors */
7046#define DO_ABS 0x02 /* checks absolute paths */
7047#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7048#define DO_ALTPATH 0x08 /* using alternate path */
7049#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7050
7051static void find_command(char *, struct cmdentry *, int, const char *);
7052
7053
7054/* ============ Hashing commands */
7055
7056/*
7057 * When commands are first encountered, they are entered in a hash table.
7058 * This ensures that a full path search will not have to be done for them
7059 * on each invocation.
7060 *
7061 * We should investigate converting to a linear search, even though that
7062 * would make the command name "hash" a misnomer.
7063 */
7064
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007065struct tblentry {
7066 struct tblentry *next; /* next entry in hash chain */
7067 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007068 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007069 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007070 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007071};
7072
Denis Vlasenko01631112007-12-16 17:20:38 +00007073static struct tblentry **cmdtable;
7074#define INIT_G_cmdtable() do { \
7075 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7076} while (0)
7077
7078static int builtinloc = -1; /* index in path of %builtin, or -1 */
7079
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007080
7081static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007082tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007083{
7084 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007085
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007086#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007087 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007088 if (APPLET_IS_NOEXEC(applet_no)) {
7089 while (*envp)
7090 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007091 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007092 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007093 /* re-exec ourselves with the new arguments */
7094 execve(bb_busybox_exec_path, argv, envp);
7095 /* If they called chroot or otherwise made the binary no longer
7096 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007097 }
7098#endif
7099
7100 repeat:
7101#ifdef SYSV
7102 do {
7103 execve(cmd, argv, envp);
7104 } while (errno == EINTR);
7105#else
7106 execve(cmd, argv, envp);
7107#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007108 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007109 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007110 return;
7111 }
7112 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007113 char **ap;
7114 char **new;
7115
7116 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007117 continue;
7118 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007119 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007120 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007121 ap += 2;
7122 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007123 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007124 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007125 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007126 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007127 goto repeat;
7128 }
7129}
7130
7131/*
7132 * Exec a program. Never returns. If you change this routine, you may
7133 * have to change the find_command routine as well.
7134 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007135static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007136static void
7137shellexec(char **argv, const char *path, int idx)
7138{
7139 char *cmdname;
7140 int e;
7141 char **envp;
7142 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007143#if ENABLE_FEATURE_SH_STANDALONE
7144 int applet_no = -1;
7145#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007146
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007147 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007148 envp = listvars(VEXPORT, VUNSET, 0);
7149 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007150#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007151 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007152#endif
7153 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007154 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007155 e = errno;
7156 } else {
7157 e = ENOENT;
7158 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7159 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007160 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007161 if (errno != ENOENT && errno != ENOTDIR)
7162 e = errno;
7163 }
7164 stunalloc(cmdname);
7165 }
7166 }
7167
7168 /* Map to POSIX errors */
7169 switch (e) {
7170 case EACCES:
7171 exerrno = 126;
7172 break;
7173 case ENOENT:
7174 exerrno = 127;
7175 break;
7176 default:
7177 exerrno = 2;
7178 break;
7179 }
7180 exitstatus = exerrno;
7181 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007182 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007183 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7184 /* NOTREACHED */
7185}
7186
7187static void
7188printentry(struct tblentry *cmdp)
7189{
7190 int idx;
7191 const char *path;
7192 char *name;
7193
7194 idx = cmdp->param.index;
7195 path = pathval();
7196 do {
7197 name = padvance(&path, cmdp->cmdname);
7198 stunalloc(name);
7199 } while (--idx >= 0);
7200 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7201}
7202
7203/*
7204 * Clear out command entries. The argument specifies the first entry in
7205 * PATH which has changed.
7206 */
7207static void
7208clearcmdentry(int firstchange)
7209{
7210 struct tblentry **tblp;
7211 struct tblentry **pp;
7212 struct tblentry *cmdp;
7213
7214 INT_OFF;
7215 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7216 pp = tblp;
7217 while ((cmdp = *pp) != NULL) {
7218 if ((cmdp->cmdtype == CMDNORMAL &&
7219 cmdp->param.index >= firstchange)
7220 || (cmdp->cmdtype == CMDBUILTIN &&
7221 builtinloc >= firstchange)
7222 ) {
7223 *pp = cmdp->next;
7224 free(cmdp);
7225 } else {
7226 pp = &cmdp->next;
7227 }
7228 }
7229 }
7230 INT_ON;
7231}
7232
7233/*
7234 * Locate a command in the command hash table. If "add" is nonzero,
7235 * add the command to the table if it is not already present. The
7236 * variable "lastcmdentry" is set to point to the address of the link
7237 * pointing to the entry, so that delete_cmd_entry can delete the
7238 * entry.
7239 *
7240 * Interrupts must be off if called with add != 0.
7241 */
7242static struct tblentry **lastcmdentry;
7243
7244static struct tblentry *
7245cmdlookup(const char *name, int add)
7246{
7247 unsigned int hashval;
7248 const char *p;
7249 struct tblentry *cmdp;
7250 struct tblentry **pp;
7251
7252 p = name;
7253 hashval = (unsigned char)*p << 4;
7254 while (*p)
7255 hashval += (unsigned char)*p++;
7256 hashval &= 0x7FFF;
7257 pp = &cmdtable[hashval % CMDTABLESIZE];
7258 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7259 if (strcmp(cmdp->cmdname, name) == 0)
7260 break;
7261 pp = &cmdp->next;
7262 }
7263 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007264 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7265 + strlen(name)
7266 /* + 1 - already done because
7267 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007268 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007269 cmdp->cmdtype = CMDUNKNOWN;
7270 strcpy(cmdp->cmdname, name);
7271 }
7272 lastcmdentry = pp;
7273 return cmdp;
7274}
7275
7276/*
7277 * Delete the command entry returned on the last lookup.
7278 */
7279static void
7280delete_cmd_entry(void)
7281{
7282 struct tblentry *cmdp;
7283
7284 INT_OFF;
7285 cmdp = *lastcmdentry;
7286 *lastcmdentry = cmdp->next;
7287 if (cmdp->cmdtype == CMDFUNCTION)
7288 freefunc(cmdp->param.func);
7289 free(cmdp);
7290 INT_ON;
7291}
7292
7293/*
7294 * Add a new command entry, replacing any existing command entry for
7295 * the same name - except special builtins.
7296 */
7297static void
7298addcmdentry(char *name, struct cmdentry *entry)
7299{
7300 struct tblentry *cmdp;
7301
7302 cmdp = cmdlookup(name, 1);
7303 if (cmdp->cmdtype == CMDFUNCTION) {
7304 freefunc(cmdp->param.func);
7305 }
7306 cmdp->cmdtype = entry->cmdtype;
7307 cmdp->param = entry->u;
7308 cmdp->rehash = 0;
7309}
7310
7311static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007312hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007313{
7314 struct tblentry **pp;
7315 struct tblentry *cmdp;
7316 int c;
7317 struct cmdentry entry;
7318 char *name;
7319
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007320 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007321 clearcmdentry(0);
7322 return 0;
7323 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007324
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007325 if (*argptr == NULL) {
7326 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7327 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7328 if (cmdp->cmdtype == CMDNORMAL)
7329 printentry(cmdp);
7330 }
7331 }
7332 return 0;
7333 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007334
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335 c = 0;
7336 while ((name = *argptr) != NULL) {
7337 cmdp = cmdlookup(name, 0);
7338 if (cmdp != NULL
7339 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007340 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7341 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007342 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007343 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007344 find_command(name, &entry, DO_ERR, pathval());
7345 if (entry.cmdtype == CMDUNKNOWN)
7346 c = 1;
7347 argptr++;
7348 }
7349 return c;
7350}
7351
7352/*
7353 * Called when a cd is done. Marks all commands so the next time they
7354 * are executed they will be rehashed.
7355 */
7356static void
7357hashcd(void)
7358{
7359 struct tblentry **pp;
7360 struct tblentry *cmdp;
7361
7362 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7363 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007364 if (cmdp->cmdtype == CMDNORMAL
7365 || (cmdp->cmdtype == CMDBUILTIN
7366 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7367 && builtinloc > 0)
7368 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007369 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007370 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007371 }
7372 }
7373}
7374
7375/*
7376 * Fix command hash table when PATH changed.
7377 * Called before PATH is changed. The argument is the new value of PATH;
7378 * pathval() still returns the old value at this point.
7379 * Called with interrupts off.
7380 */
7381static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007382changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007383{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007384 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007385 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007386 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007387 int idx_bltin;
7388
7389 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 firstchange = 9999; /* assume no change */
7391 idx = 0;
7392 idx_bltin = -1;
7393 for (;;) {
7394 if (*old != *new) {
7395 firstchange = idx;
7396 if ((*old == '\0' && *new == ':')
7397 || (*old == ':' && *new == '\0'))
7398 firstchange++;
7399 old = new; /* ignore subsequent differences */
7400 }
7401 if (*new == '\0')
7402 break;
7403 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7404 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007405 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007406 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407 new++, old++;
7408 }
7409 if (builtinloc < 0 && idx_bltin >= 0)
7410 builtinloc = idx_bltin; /* zap builtins */
7411 if (builtinloc >= 0 && idx_bltin < 0)
7412 firstchange = 0;
7413 clearcmdentry(firstchange);
7414 builtinloc = idx_bltin;
7415}
7416
7417#define TEOF 0
7418#define TNL 1
7419#define TREDIR 2
7420#define TWORD 3
7421#define TSEMI 4
7422#define TBACKGND 5
7423#define TAND 6
7424#define TOR 7
7425#define TPIPE 8
7426#define TLP 9
7427#define TRP 10
7428#define TENDCASE 11
7429#define TENDBQUOTE 12
7430#define TNOT 13
7431#define TCASE 14
7432#define TDO 15
7433#define TDONE 16
7434#define TELIF 17
7435#define TELSE 18
7436#define TESAC 19
7437#define TFI 20
7438#define TFOR 21
7439#define TIF 22
7440#define TIN 23
7441#define TTHEN 24
7442#define TUNTIL 25
7443#define TWHILE 26
7444#define TBEGIN 27
7445#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007446typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007447
7448/* first char is indicating which tokens mark the end of a list */
7449static const char *const tokname_array[] = {
7450 "\1end of file",
7451 "\0newline",
7452 "\0redirection",
7453 "\0word",
7454 "\0;",
7455 "\0&",
7456 "\0&&",
7457 "\0||",
7458 "\0|",
7459 "\0(",
7460 "\1)",
7461 "\1;;",
7462 "\1`",
7463#define KWDOFFSET 13
7464 /* the following are keywords */
7465 "\0!",
7466 "\0case",
7467 "\1do",
7468 "\1done",
7469 "\1elif",
7470 "\1else",
7471 "\1esac",
7472 "\1fi",
7473 "\0for",
7474 "\0if",
7475 "\0in",
7476 "\1then",
7477 "\0until",
7478 "\0while",
7479 "\0{",
7480 "\1}",
7481};
7482
7483static const char *
7484tokname(int tok)
7485{
7486 static char buf[16];
7487
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007488//try this:
7489//if (tok < TSEMI) return tokname_array[tok] + 1;
7490//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7491//return buf;
7492
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007493 if (tok >= TSEMI)
7494 buf[0] = '"';
7495 sprintf(buf + (tok >= TSEMI), "%s%c",
7496 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7497 return buf;
7498}
7499
7500/* Wrapper around strcmp for qsort/bsearch/... */
7501static int
7502pstrcmp(const void *a, const void *b)
7503{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007504 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007505}
7506
7507static const char *const *
7508findkwd(const char *s)
7509{
7510 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007511 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7512 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007513}
7514
7515/*
7516 * Locate and print what a word is...
7517 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007518static int
7519describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007520{
7521 struct cmdentry entry;
7522 struct tblentry *cmdp;
7523#if ENABLE_ASH_ALIAS
7524 const struct alias *ap;
7525#endif
7526 const char *path = pathval();
7527
7528 if (describe_command_verbose) {
7529 out1str(command);
7530 }
7531
7532 /* First look at the keywords */
7533 if (findkwd(command)) {
7534 out1str(describe_command_verbose ? " is a shell keyword" : command);
7535 goto out;
7536 }
7537
7538#if ENABLE_ASH_ALIAS
7539 /* Then look at the aliases */
7540 ap = lookupalias(command, 0);
7541 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007542 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007543 out1str("alias ");
7544 printalias(ap);
7545 return 0;
7546 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007547 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007548 goto out;
7549 }
7550#endif
7551 /* Then check if it is a tracked alias */
7552 cmdp = cmdlookup(command, 0);
7553 if (cmdp != NULL) {
7554 entry.cmdtype = cmdp->cmdtype;
7555 entry.u = cmdp->param;
7556 } else {
7557 /* Finally use brute force */
7558 find_command(command, &entry, DO_ABS, path);
7559 }
7560
7561 switch (entry.cmdtype) {
7562 case CMDNORMAL: {
7563 int j = entry.u.index;
7564 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007565 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007566 p = command;
7567 } else {
7568 do {
7569 p = padvance(&path, command);
7570 stunalloc(p);
7571 } while (--j >= 0);
7572 }
7573 if (describe_command_verbose) {
7574 out1fmt(" is%s %s",
7575 (cmdp ? " a tracked alias for" : nullstr), p
7576 );
7577 } else {
7578 out1str(p);
7579 }
7580 break;
7581 }
7582
7583 case CMDFUNCTION:
7584 if (describe_command_verbose) {
7585 out1str(" is a shell function");
7586 } else {
7587 out1str(command);
7588 }
7589 break;
7590
7591 case CMDBUILTIN:
7592 if (describe_command_verbose) {
7593 out1fmt(" is a %sshell builtin",
7594 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7595 "special " : nullstr
7596 );
7597 } else {
7598 out1str(command);
7599 }
7600 break;
7601
7602 default:
7603 if (describe_command_verbose) {
7604 out1str(": not found\n");
7605 }
7606 return 127;
7607 }
7608 out:
7609 outstr("\n", stdout);
7610 return 0;
7611}
7612
7613static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007614typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007615{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007616 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007617 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007618 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007619
Denis Vlasenko46846e22007-05-20 13:08:31 +00007620 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007621 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007622 i++;
7623 verbose = 0;
7624 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007625 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007626 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007627 }
7628 return err;
7629}
7630
7631#if ENABLE_ASH_CMDCMD
7632static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007633commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007634{
7635 int c;
7636 enum {
7637 VERIFY_BRIEF = 1,
7638 VERIFY_VERBOSE = 2,
7639 } verify = 0;
7640
7641 while ((c = nextopt("pvV")) != '\0')
7642 if (c == 'V')
7643 verify |= VERIFY_VERBOSE;
7644 else if (c == 'v')
7645 verify |= VERIFY_BRIEF;
7646#if DEBUG
7647 else if (c != 'p')
7648 abort();
7649#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007650 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7651 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007652 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007653 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007654
7655 return 0;
7656}
7657#endif
7658
7659
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007660/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007661
Denis Vlasenko340299a2008-11-21 10:36:36 +00007662static int funcblocksize; /* size of structures in function */
7663static int funcstringsize; /* size of strings in node */
7664static void *funcblock; /* block to allocate function from */
7665static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007666
Eric Andersencb57d552001-06-28 07:25:16 +00007667/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007668#define EV_EXIT 01 /* exit after evaluating tree */
7669#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007670#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007671
Denis Vlasenko340299a2008-11-21 10:36:36 +00007672static const short nodesize[N_NUMBER] = {
7673 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7674 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7675 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7676 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7677 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7678 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7679 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7680 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7681 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7682 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7683 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7684 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7685 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7686 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7687 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7688 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7689 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007690#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007691 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007692#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007693 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7694 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7695 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7696 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7697 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7698 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7699 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7700 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7701 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007702};
7703
7704static void calcsize(union node *n);
7705
7706static void
7707sizenodelist(struct nodelist *lp)
7708{
7709 while (lp) {
7710 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7711 calcsize(lp->n);
7712 lp = lp->next;
7713 }
7714}
7715
7716static void
7717calcsize(union node *n)
7718{
7719 if (n == NULL)
7720 return;
7721 funcblocksize += nodesize[n->type];
7722 switch (n->type) {
7723 case NCMD:
7724 calcsize(n->ncmd.redirect);
7725 calcsize(n->ncmd.args);
7726 calcsize(n->ncmd.assign);
7727 break;
7728 case NPIPE:
7729 sizenodelist(n->npipe.cmdlist);
7730 break;
7731 case NREDIR:
7732 case NBACKGND:
7733 case NSUBSHELL:
7734 calcsize(n->nredir.redirect);
7735 calcsize(n->nredir.n);
7736 break;
7737 case NAND:
7738 case NOR:
7739 case NSEMI:
7740 case NWHILE:
7741 case NUNTIL:
7742 calcsize(n->nbinary.ch2);
7743 calcsize(n->nbinary.ch1);
7744 break;
7745 case NIF:
7746 calcsize(n->nif.elsepart);
7747 calcsize(n->nif.ifpart);
7748 calcsize(n->nif.test);
7749 break;
7750 case NFOR:
7751 funcstringsize += strlen(n->nfor.var) + 1;
7752 calcsize(n->nfor.body);
7753 calcsize(n->nfor.args);
7754 break;
7755 case NCASE:
7756 calcsize(n->ncase.cases);
7757 calcsize(n->ncase.expr);
7758 break;
7759 case NCLIST:
7760 calcsize(n->nclist.body);
7761 calcsize(n->nclist.pattern);
7762 calcsize(n->nclist.next);
7763 break;
7764 case NDEFUN:
7765 case NARG:
7766 sizenodelist(n->narg.backquote);
7767 funcstringsize += strlen(n->narg.text) + 1;
7768 calcsize(n->narg.next);
7769 break;
7770 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007771#if ENABLE_ASH_BASH_COMPAT
7772 case NTO2:
7773#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007774 case NCLOBBER:
7775 case NFROM:
7776 case NFROMTO:
7777 case NAPPEND:
7778 calcsize(n->nfile.fname);
7779 calcsize(n->nfile.next);
7780 break;
7781 case NTOFD:
7782 case NFROMFD:
7783 calcsize(n->ndup.vname);
7784 calcsize(n->ndup.next);
7785 break;
7786 case NHERE:
7787 case NXHERE:
7788 calcsize(n->nhere.doc);
7789 calcsize(n->nhere.next);
7790 break;
7791 case NNOT:
7792 calcsize(n->nnot.com);
7793 break;
7794 };
7795}
7796
7797static char *
7798nodeckstrdup(char *s)
7799{
7800 char *rtn = funcstring;
7801
7802 strcpy(funcstring, s);
7803 funcstring += strlen(s) + 1;
7804 return rtn;
7805}
7806
7807static union node *copynode(union node *);
7808
7809static struct nodelist *
7810copynodelist(struct nodelist *lp)
7811{
7812 struct nodelist *start;
7813 struct nodelist **lpp;
7814
7815 lpp = &start;
7816 while (lp) {
7817 *lpp = funcblock;
7818 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7819 (*lpp)->n = copynode(lp->n);
7820 lp = lp->next;
7821 lpp = &(*lpp)->next;
7822 }
7823 *lpp = NULL;
7824 return start;
7825}
7826
7827static union node *
7828copynode(union node *n)
7829{
7830 union node *new;
7831
7832 if (n == NULL)
7833 return NULL;
7834 new = funcblock;
7835 funcblock = (char *) funcblock + nodesize[n->type];
7836
7837 switch (n->type) {
7838 case NCMD:
7839 new->ncmd.redirect = copynode(n->ncmd.redirect);
7840 new->ncmd.args = copynode(n->ncmd.args);
7841 new->ncmd.assign = copynode(n->ncmd.assign);
7842 break;
7843 case NPIPE:
7844 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007845 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007846 break;
7847 case NREDIR:
7848 case NBACKGND:
7849 case NSUBSHELL:
7850 new->nredir.redirect = copynode(n->nredir.redirect);
7851 new->nredir.n = copynode(n->nredir.n);
7852 break;
7853 case NAND:
7854 case NOR:
7855 case NSEMI:
7856 case NWHILE:
7857 case NUNTIL:
7858 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7859 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7860 break;
7861 case NIF:
7862 new->nif.elsepart = copynode(n->nif.elsepart);
7863 new->nif.ifpart = copynode(n->nif.ifpart);
7864 new->nif.test = copynode(n->nif.test);
7865 break;
7866 case NFOR:
7867 new->nfor.var = nodeckstrdup(n->nfor.var);
7868 new->nfor.body = copynode(n->nfor.body);
7869 new->nfor.args = copynode(n->nfor.args);
7870 break;
7871 case NCASE:
7872 new->ncase.cases = copynode(n->ncase.cases);
7873 new->ncase.expr = copynode(n->ncase.expr);
7874 break;
7875 case NCLIST:
7876 new->nclist.body = copynode(n->nclist.body);
7877 new->nclist.pattern = copynode(n->nclist.pattern);
7878 new->nclist.next = copynode(n->nclist.next);
7879 break;
7880 case NDEFUN:
7881 case NARG:
7882 new->narg.backquote = copynodelist(n->narg.backquote);
7883 new->narg.text = nodeckstrdup(n->narg.text);
7884 new->narg.next = copynode(n->narg.next);
7885 break;
7886 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007887#if ENABLE_ASH_BASH_COMPAT
7888 case NTO2:
7889#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007890 case NCLOBBER:
7891 case NFROM:
7892 case NFROMTO:
7893 case NAPPEND:
7894 new->nfile.fname = copynode(n->nfile.fname);
7895 new->nfile.fd = n->nfile.fd;
7896 new->nfile.next = copynode(n->nfile.next);
7897 break;
7898 case NTOFD:
7899 case NFROMFD:
7900 new->ndup.vname = copynode(n->ndup.vname);
7901 new->ndup.dupfd = n->ndup.dupfd;
7902 new->ndup.fd = n->ndup.fd;
7903 new->ndup.next = copynode(n->ndup.next);
7904 break;
7905 case NHERE:
7906 case NXHERE:
7907 new->nhere.doc = copynode(n->nhere.doc);
7908 new->nhere.fd = n->nhere.fd;
7909 new->nhere.next = copynode(n->nhere.next);
7910 break;
7911 case NNOT:
7912 new->nnot.com = copynode(n->nnot.com);
7913 break;
7914 };
7915 new->type = n->type;
7916 return new;
7917}
7918
7919/*
7920 * Make a copy of a parse tree.
7921 */
7922static struct funcnode *
7923copyfunc(union node *n)
7924{
7925 struct funcnode *f;
7926 size_t blocksize;
7927
7928 funcblocksize = offsetof(struct funcnode, n);
7929 funcstringsize = 0;
7930 calcsize(n);
7931 blocksize = funcblocksize;
7932 f = ckmalloc(blocksize + funcstringsize);
7933 funcblock = (char *) f + offsetof(struct funcnode, n);
7934 funcstring = (char *) f + blocksize;
7935 copynode(n);
7936 f->count = 0;
7937 return f;
7938}
7939
7940/*
7941 * Define a shell function.
7942 */
7943static void
7944defun(char *name, union node *func)
7945{
7946 struct cmdentry entry;
7947
7948 INT_OFF;
7949 entry.cmdtype = CMDFUNCTION;
7950 entry.u.func = copyfunc(func);
7951 addcmdentry(name, &entry);
7952 INT_ON;
7953}
7954
Denis Vlasenko4b875702009-03-19 13:30:04 +00007955/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007956#define SKIPBREAK (1 << 0)
7957#define SKIPCONT (1 << 1)
7958#define SKIPFUNC (1 << 2)
7959#define SKIPFILE (1 << 3)
7960#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00007961static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007962static int skipcount; /* number of levels to skip */
7963static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007964static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007965
Denis Vlasenko4b875702009-03-19 13:30:04 +00007966/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007967static int evalstring(char *s, int mask);
7968
Denis Vlasenko4b875702009-03-19 13:30:04 +00007969/* Called to execute a trap.
7970 * Single callsite - at the end of evaltree().
7971 * If we return non-zero, exaltree raises EXEXIT exception.
7972 *
7973 * Perhaps we should avoid entering new trap handlers
7974 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007975 */
7976static int
7977dotrap(void)
7978{
Denis Vlasenko4b875702009-03-19 13:30:04 +00007979 uint8_t *g;
7980 int sig;
7981 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007982
7983 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007984 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007985 xbarrier();
7986
Denis Vlasenko653d8e72009-03-19 21:59:35 +00007987 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00007988 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
7989 int want_exexit;
7990 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007991
Denis Vlasenko4b875702009-03-19 13:30:04 +00007992 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007993 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00007994 t = trap[sig];
7995 /* non-trapped SIGINT is handled separately by raise_interrupt,
7996 * don't upset it by resetting gotsig[SIGINT-1] */
7997 if (sig == SIGINT && !t)
7998 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00007999
8000 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008001 *g = 0;
8002 if (!t)
8003 continue;
8004 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008005 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008006 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008007 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008008 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008009 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008010 }
8011
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008012 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008013 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008014}
8015
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008016/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00008017static void evalloop(union node *, int);
8018static void evalfor(union node *, int);
8019static void evalcase(union node *, int);
8020static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008021static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00008022static void evalpipe(union node *, int);
8023static void evalcommand(union node *, int);
8024static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008025static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008026
Eric Andersen62483552001-07-10 06:09:16 +00008027/*
Eric Andersenc470f442003-07-28 09:56:35 +00008028 * Evaluate a parse tree. The value is left in the global variable
8029 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008030 */
Eric Andersenc470f442003-07-28 09:56:35 +00008031static void
8032evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008033{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008034 struct jmploc *volatile savehandler = exception_handler;
8035 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008036 int checkexit = 0;
8037 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008038 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008039 int int_level;
8040
8041 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008042
Eric Andersenc470f442003-07-28 09:56:35 +00008043 if (n == NULL) {
8044 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008045 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008046 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008047 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008048
8049 exception_handler = &jmploc;
8050 {
8051 int err = setjmp(jmploc.loc);
8052 if (err) {
8053 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008054 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008055 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8056 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008057 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008058 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008059 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008060 TRACE(("exception %d in evaltree, propagating err=%d\n",
8061 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008062 exception_handler = savehandler;
8063 longjmp(exception_handler->loc, err);
8064 }
8065 }
8066
Eric Andersenc470f442003-07-28 09:56:35 +00008067 switch (n->type) {
8068 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008069#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008070 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008071 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008072 break;
8073#endif
8074 case NNOT:
8075 evaltree(n->nnot.com, EV_TESTED);
8076 status = !exitstatus;
8077 goto setstatus;
8078 case NREDIR:
8079 expredir(n->nredir.redirect);
8080 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8081 if (!status) {
8082 evaltree(n->nredir.n, flags & EV_TESTED);
8083 status = exitstatus;
8084 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008085 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008086 goto setstatus;
8087 case NCMD:
8088 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008089 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008090 if (eflag && !(flags & EV_TESTED))
8091 checkexit = ~0;
8092 goto calleval;
8093 case NFOR:
8094 evalfn = evalfor;
8095 goto calleval;
8096 case NWHILE:
8097 case NUNTIL:
8098 evalfn = evalloop;
8099 goto calleval;
8100 case NSUBSHELL:
8101 case NBACKGND:
8102 evalfn = evalsubshell;
8103 goto calleval;
8104 case NPIPE:
8105 evalfn = evalpipe;
8106 goto checkexit;
8107 case NCASE:
8108 evalfn = evalcase;
8109 goto calleval;
8110 case NAND:
8111 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008112 case NSEMI: {
8113
Eric Andersenc470f442003-07-28 09:56:35 +00008114#if NAND + 1 != NOR
8115#error NAND + 1 != NOR
8116#endif
8117#if NOR + 1 != NSEMI
8118#error NOR + 1 != NSEMI
8119#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008120 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008121 evaltree(
8122 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008123 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008124 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008125 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008126 break;
8127 if (!evalskip) {
8128 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008129 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008130 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008131 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008132 evalfn(n, flags);
8133 break;
8134 }
8135 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008136 }
Eric Andersenc470f442003-07-28 09:56:35 +00008137 case NIF:
8138 evaltree(n->nif.test, EV_TESTED);
8139 if (evalskip)
8140 break;
8141 if (exitstatus == 0) {
8142 n = n->nif.ifpart;
8143 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008144 }
8145 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008146 n = n->nif.elsepart;
8147 goto evaln;
8148 }
8149 goto success;
8150 case NDEFUN:
8151 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008152 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008153 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008154 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008155 exitstatus = status;
8156 break;
8157 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008158
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008159 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008160 exception_handler = savehandler;
8161 out1:
8162 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008163 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008164 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008165 goto exexit;
8166
8167 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008168 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008169 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008170 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008171
8172 RESTORE_INT(int_level);
8173 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008174}
8175
Eric Andersenc470f442003-07-28 09:56:35 +00008176#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8177static
8178#endif
8179void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8180
Eric Andersenc470f442003-07-28 09:56:35 +00008181static void
8182evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008183{
8184 int status;
8185
8186 loopnest++;
8187 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008188 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008189 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008190 int i;
8191
Eric Andersencb57d552001-06-28 07:25:16 +00008192 evaltree(n->nbinary.ch1, EV_TESTED);
8193 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008194 skipping:
8195 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008196 evalskip = 0;
8197 continue;
8198 }
8199 if (evalskip == SKIPBREAK && --skipcount <= 0)
8200 evalskip = 0;
8201 break;
8202 }
Eric Andersenc470f442003-07-28 09:56:35 +00008203 i = exitstatus;
8204 if (n->type != NWHILE)
8205 i = !i;
8206 if (i != 0)
8207 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008208 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008209 status = exitstatus;
8210 if (evalskip)
8211 goto skipping;
8212 }
8213 loopnest--;
8214 exitstatus = status;
8215}
8216
Eric Andersenc470f442003-07-28 09:56:35 +00008217static void
8218evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008219{
8220 struct arglist arglist;
8221 union node *argp;
8222 struct strlist *sp;
8223 struct stackmark smark;
8224
8225 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008226 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008227 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008228 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008229 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008230 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008231 if (evalskip)
8232 goto out;
8233 }
8234 *arglist.lastp = NULL;
8235
8236 exitstatus = 0;
8237 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008238 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008239 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008240 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008241 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008242 if (evalskip) {
8243 if (evalskip == SKIPCONT && --skipcount <= 0) {
8244 evalskip = 0;
8245 continue;
8246 }
8247 if (evalskip == SKIPBREAK && --skipcount <= 0)
8248 evalskip = 0;
8249 break;
8250 }
8251 }
8252 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008253 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008254 popstackmark(&smark);
8255}
8256
Eric Andersenc470f442003-07-28 09:56:35 +00008257static void
8258evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008259{
8260 union node *cp;
8261 union node *patp;
8262 struct arglist arglist;
8263 struct stackmark smark;
8264
8265 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008266 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008267 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008268 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008269 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008270 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8271 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008272 if (casematch(patp, arglist.list->text)) {
8273 if (evalskip == 0) {
8274 evaltree(cp->nclist.body, flags);
8275 }
8276 goto out;
8277 }
8278 }
8279 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008280 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008281 popstackmark(&smark);
8282}
8283
Eric Andersenc470f442003-07-28 09:56:35 +00008284/*
8285 * Kick off a subshell to evaluate a tree.
8286 */
Eric Andersenc470f442003-07-28 09:56:35 +00008287static void
8288evalsubshell(union node *n, int flags)
8289{
8290 struct job *jp;
8291 int backgnd = (n->type == NBACKGND);
8292 int status;
8293
8294 expredir(n->nredir.redirect);
8295 if (!backgnd && flags & EV_EXIT && !trap[0])
8296 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008297 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008298 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008299 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008300 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008301 flags |= EV_EXIT;
8302 if (backgnd)
8303 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008304 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008305 redirect(n->nredir.redirect, 0);
8306 evaltreenr(n->nredir.n, flags);
8307 /* never returns */
8308 }
8309 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008310 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008311 status = waitforjob(jp);
8312 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008313 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008314}
8315
Eric Andersenc470f442003-07-28 09:56:35 +00008316/*
8317 * Compute the names of the files in a redirection list.
8318 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008319static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008320static void
8321expredir(union node *n)
8322{
8323 union node *redir;
8324
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008325 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008326 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008327
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008328 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008329 fn.lastp = &fn.list;
8330 switch (redir->type) {
8331 case NFROMTO:
8332 case NFROM:
8333 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008334#if ENABLE_ASH_BASH_COMPAT
8335 case NTO2:
8336#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008337 case NCLOBBER:
8338 case NAPPEND:
8339 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008340#if ENABLE_ASH_BASH_COMPAT
8341 store_expfname:
8342#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008343 redir->nfile.expfname = fn.list->text;
8344 break;
8345 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008346 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008347 if (redir->ndup.vname) {
8348 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008349 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008350 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008351#if ENABLE_ASH_BASH_COMPAT
8352//FIXME: we used expandarg with different args!
8353 if (!isdigit_str9(fn.list->text)) {
8354 /* >&file, not >&fd */
8355 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8356 ash_msg_and_raise_error("redir error");
8357 redir->type = NTO2;
8358 goto store_expfname;
8359 }
8360#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008361 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008362 }
8363 break;
8364 }
8365 }
8366}
8367
Eric Andersencb57d552001-06-28 07:25:16 +00008368/*
Eric Andersencb57d552001-06-28 07:25:16 +00008369 * Evaluate a pipeline. All the processes in the pipeline are children
8370 * of the process creating the pipeline. (This differs from some versions
8371 * of the shell, which make the last process in a pipeline the parent
8372 * of all the rest.)
8373 */
Eric Andersenc470f442003-07-28 09:56:35 +00008374static void
8375evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008376{
8377 struct job *jp;
8378 struct nodelist *lp;
8379 int pipelen;
8380 int prevfd;
8381 int pip[2];
8382
Eric Andersenc470f442003-07-28 09:56:35 +00008383 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008384 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008385 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008386 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008387 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008388 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008389 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008390 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008391 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008392 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008393 pip[1] = -1;
8394 if (lp->next) {
8395 if (pipe(pip) < 0) {
8396 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008397 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008398 }
8399 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008400 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008401 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008402 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008403 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008404 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008405 if (prevfd > 0) {
8406 dup2(prevfd, 0);
8407 close(prevfd);
8408 }
8409 if (pip[1] > 1) {
8410 dup2(pip[1], 1);
8411 close(pip[1]);
8412 }
Eric Andersenc470f442003-07-28 09:56:35 +00008413 evaltreenr(lp->n, flags);
8414 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008415 }
8416 if (prevfd >= 0)
8417 close(prevfd);
8418 prevfd = pip[0];
8419 close(pip[1]);
8420 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008421 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008422 exitstatus = waitforjob(jp);
8423 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008424 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008425 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008426}
8427
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008428/*
8429 * Controls whether the shell is interactive or not.
8430 */
8431static void
8432setinteractive(int on)
8433{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008434 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008435
8436 if (++on == is_interactive)
8437 return;
8438 is_interactive = on;
8439 setsignal(SIGINT);
8440 setsignal(SIGQUIT);
8441 setsignal(SIGTERM);
8442#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8443 if (is_interactive > 1) {
8444 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008445 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008446
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008447 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008448 out1fmt(
8449 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008450 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008451 "Enter 'help' for a list of built-in commands."
8452 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008453 bb_banner);
8454 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008455 }
8456 }
8457#endif
8458}
8459
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008460static void
8461optschanged(void)
8462{
8463#if DEBUG
8464 opentrace();
8465#endif
8466 setinteractive(iflag);
8467 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008468#if ENABLE_FEATURE_EDITING_VI
8469 if (viflag)
8470 line_input_state->flags |= VI_MODE;
8471 else
8472 line_input_state->flags &= ~VI_MODE;
8473#else
8474 viflag = 0; /* forcibly keep the option off */
8475#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008476}
8477
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008478static struct localvar *localvars;
8479
8480/*
8481 * Called after a function returns.
8482 * Interrupts must be off.
8483 */
8484static void
8485poplocalvars(void)
8486{
8487 struct localvar *lvp;
8488 struct var *vp;
8489
8490 while ((lvp = localvars) != NULL) {
8491 localvars = lvp->next;
8492 vp = lvp->vp;
8493 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8494 if (vp == NULL) { /* $- saved */
8495 memcpy(optlist, lvp->text, sizeof(optlist));
8496 free((char*)lvp->text);
8497 optschanged();
8498 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8499 unsetvar(vp->text);
8500 } else {
8501 if (vp->func)
8502 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8503 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8504 free((char*)vp->text);
8505 vp->flags = lvp->flags;
8506 vp->text = lvp->text;
8507 }
8508 free(lvp);
8509 }
8510}
8511
8512static int
8513evalfun(struct funcnode *func, int argc, char **argv, int flags)
8514{
8515 volatile struct shparam saveparam;
8516 struct localvar *volatile savelocalvars;
8517 struct jmploc *volatile savehandler;
8518 struct jmploc jmploc;
8519 int e;
8520
8521 saveparam = shellparam;
8522 savelocalvars = localvars;
8523 e = setjmp(jmploc.loc);
8524 if (e) {
8525 goto funcdone;
8526 }
8527 INT_OFF;
8528 savehandler = exception_handler;
8529 exception_handler = &jmploc;
8530 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008531 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008532 func->count++;
8533 funcnest++;
8534 INT_ON;
8535 shellparam.nparam = argc - 1;
8536 shellparam.p = argv + 1;
8537#if ENABLE_ASH_GETOPTS
8538 shellparam.optind = 1;
8539 shellparam.optoff = -1;
8540#endif
8541 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008542 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008543 INT_OFF;
8544 funcnest--;
8545 freefunc(func);
8546 poplocalvars();
8547 localvars = savelocalvars;
8548 freeparam(&shellparam);
8549 shellparam = saveparam;
8550 exception_handler = savehandler;
8551 INT_ON;
8552 evalskip &= ~SKIPFUNC;
8553 return e;
8554}
8555
Denis Vlasenko131ae172007-02-18 13:00:19 +00008556#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008557static char **
8558parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008559{
8560 char *cp, c;
8561
8562 for (;;) {
8563 cp = *++argv;
8564 if (!cp)
8565 return 0;
8566 if (*cp++ != '-')
8567 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008568 c = *cp++;
8569 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008570 break;
8571 if (c == '-' && !*cp) {
8572 argv++;
8573 break;
8574 }
8575 do {
8576 switch (c) {
8577 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008578 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008579 break;
8580 default:
8581 /* run 'typecmd' for other options */
8582 return 0;
8583 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008584 c = *cp++;
8585 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008586 }
8587 return argv;
8588}
8589#endif
8590
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008591/*
8592 * Make a variable a local variable. When a variable is made local, it's
8593 * value and flags are saved in a localvar structure. The saved values
8594 * will be restored when the shell function returns. We handle the name
8595 * "-" as a special case.
8596 */
8597static void
8598mklocal(char *name)
8599{
8600 struct localvar *lvp;
8601 struct var **vpp;
8602 struct var *vp;
8603
8604 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008605 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008606 if (LONE_DASH(name)) {
8607 char *p;
8608 p = ckmalloc(sizeof(optlist));
8609 lvp->text = memcpy(p, optlist, sizeof(optlist));
8610 vp = NULL;
8611 } else {
8612 char *eq;
8613
8614 vpp = hashvar(name);
8615 vp = *findvar(vpp, name);
8616 eq = strchr(name, '=');
8617 if (vp == NULL) {
8618 if (eq)
8619 setvareq(name, VSTRFIXED);
8620 else
8621 setvar(name, NULL, VSTRFIXED);
8622 vp = *vpp; /* the new variable */
8623 lvp->flags = VUNSET;
8624 } else {
8625 lvp->text = vp->text;
8626 lvp->flags = vp->flags;
8627 vp->flags |= VSTRFIXED|VTEXTFIXED;
8628 if (eq)
8629 setvareq(name, 0);
8630 }
8631 }
8632 lvp->vp = vp;
8633 lvp->next = localvars;
8634 localvars = lvp;
8635 INT_ON;
8636}
8637
8638/*
8639 * The "local" command.
8640 */
8641static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008642localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008643{
8644 char *name;
8645
8646 argv = argptr;
8647 while ((name = *argv++) != NULL) {
8648 mklocal(name);
8649 }
8650 return 0;
8651}
8652
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008653static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008654falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008655{
8656 return 1;
8657}
8658
8659static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008660truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008661{
8662 return 0;
8663}
8664
8665static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008666execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008667{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008668 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008669 iflag = 0; /* exit on error */
8670 mflag = 0;
8671 optschanged();
8672 shellexec(argv + 1, pathval(), 0);
8673 }
8674 return 0;
8675}
8676
8677/*
8678 * The return command.
8679 */
8680static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008681returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008682{
8683 /*
8684 * If called outside a function, do what ksh does;
8685 * skip the rest of the file.
8686 */
8687 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8688 return argv[1] ? number(argv[1]) : exitstatus;
8689}
8690
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008691/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008692static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008693static int dotcmd(int, char **);
8694static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008695static int exitcmd(int, char **);
8696static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008697#if ENABLE_ASH_GETOPTS
8698static int getoptscmd(int, char **);
8699#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008700#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008701static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008702#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008703#if ENABLE_ASH_MATH_SUPPORT
8704static int letcmd(int, char **);
8705#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008706static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008707static int setcmd(int, char **);
8708static int shiftcmd(int, char **);
8709static int timescmd(int, char **);
8710static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008711static int umaskcmd(int, char **);
8712static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008713static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008714
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008715#define BUILTIN_NOSPEC "0"
8716#define BUILTIN_SPECIAL "1"
8717#define BUILTIN_REGULAR "2"
8718#define BUILTIN_SPEC_REG "3"
8719#define BUILTIN_ASSIGN "4"
8720#define BUILTIN_SPEC_ASSG "5"
8721#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008722#define BUILTIN_SPEC_REG_ASSG "7"
8723
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008724/* We do not handle [[ expr ]] bashism bash-compatibly,
8725 * we make it a synonym of [ expr ].
8726 * Basically, word splitting and pathname expansion should NOT be performed
8727 * Examples:
8728 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8729 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8730 * Additional operators:
8731 * || and && should work as -o and -a
8732 * =~ regexp match
8733 * Apart from the above, [[ expr ]] should work as [ expr ]
8734 */
8735
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008736#define echocmd echo_main
8737#define printfcmd printf_main
8738#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008739
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008740/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008741static const struct builtincmd builtintab[] = {
8742 { BUILTIN_SPEC_REG ".", dotcmd },
8743 { BUILTIN_SPEC_REG ":", truecmd },
8744#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008745 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008746#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008747 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008748#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008749#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008750#if ENABLE_ASH_ALIAS
8751 { BUILTIN_REG_ASSG "alias", aliascmd },
8752#endif
8753#if JOBS
8754 { BUILTIN_REGULAR "bg", fg_bgcmd },
8755#endif
8756 { BUILTIN_SPEC_REG "break", breakcmd },
8757 { BUILTIN_REGULAR "cd", cdcmd },
8758 { BUILTIN_NOSPEC "chdir", cdcmd },
8759#if ENABLE_ASH_CMDCMD
8760 { BUILTIN_REGULAR "command", commandcmd },
8761#endif
8762 { BUILTIN_SPEC_REG "continue", breakcmd },
8763#if ENABLE_ASH_BUILTIN_ECHO
8764 { BUILTIN_REGULAR "echo", echocmd },
8765#endif
8766 { BUILTIN_SPEC_REG "eval", evalcmd },
8767 { BUILTIN_SPEC_REG "exec", execcmd },
8768 { BUILTIN_SPEC_REG "exit", exitcmd },
8769 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8770 { BUILTIN_REGULAR "false", falsecmd },
8771#if JOBS
8772 { BUILTIN_REGULAR "fg", fg_bgcmd },
8773#endif
8774#if ENABLE_ASH_GETOPTS
8775 { BUILTIN_REGULAR "getopts", getoptscmd },
8776#endif
8777 { BUILTIN_NOSPEC "hash", hashcmd },
8778#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8779 { BUILTIN_NOSPEC "help", helpcmd },
8780#endif
8781#if JOBS
8782 { BUILTIN_REGULAR "jobs", jobscmd },
8783 { BUILTIN_REGULAR "kill", killcmd },
8784#endif
8785#if ENABLE_ASH_MATH_SUPPORT
8786 { BUILTIN_NOSPEC "let", letcmd },
8787#endif
8788 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008789#if ENABLE_ASH_BUILTIN_PRINTF
8790 { BUILTIN_REGULAR "printf", printfcmd },
8791#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008792 { BUILTIN_NOSPEC "pwd", pwdcmd },
8793 { BUILTIN_REGULAR "read", readcmd },
8794 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8795 { BUILTIN_SPEC_REG "return", returncmd },
8796 { BUILTIN_SPEC_REG "set", setcmd },
8797 { BUILTIN_SPEC_REG "shift", shiftcmd },
8798 { BUILTIN_SPEC_REG "source", dotcmd },
8799#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008800 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008801#endif
8802 { BUILTIN_SPEC_REG "times", timescmd },
8803 { BUILTIN_SPEC_REG "trap", trapcmd },
8804 { BUILTIN_REGULAR "true", truecmd },
8805 { BUILTIN_NOSPEC "type", typecmd },
8806 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8807 { BUILTIN_REGULAR "umask", umaskcmd },
8808#if ENABLE_ASH_ALIAS
8809 { BUILTIN_REGULAR "unalias", unaliascmd },
8810#endif
8811 { BUILTIN_SPEC_REG "unset", unsetcmd },
8812 { BUILTIN_REGULAR "wait", waitcmd },
8813};
8814
Denis Vlasenko80591b02008-03-25 07:49:43 +00008815/* Should match the above table! */
8816#define COMMANDCMD (builtintab + \
8817 2 + \
8818 1 * ENABLE_ASH_BUILTIN_TEST + \
8819 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8820 1 * ENABLE_ASH_ALIAS + \
8821 1 * ENABLE_ASH_JOB_CONTROL + \
8822 3)
8823#define EXECCMD (builtintab + \
8824 2 + \
8825 1 * ENABLE_ASH_BUILTIN_TEST + \
8826 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8827 1 * ENABLE_ASH_ALIAS + \
8828 1 * ENABLE_ASH_JOB_CONTROL + \
8829 3 + \
8830 1 * ENABLE_ASH_CMDCMD + \
8831 1 + \
8832 ENABLE_ASH_BUILTIN_ECHO + \
8833 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008834
8835/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008836 * Search the table of builtin commands.
8837 */
8838static struct builtincmd *
8839find_builtin(const char *name)
8840{
8841 struct builtincmd *bp;
8842
8843 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008844 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008845 pstrcmp
8846 );
8847 return bp;
8848}
8849
8850/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008851 * Execute a simple command.
8852 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008853static int
8854isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008855{
8856 const char *q = endofname(p);
8857 if (p == q)
8858 return 0;
8859 return *q == '=';
8860}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008861static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008862bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008863{
8864 /* Preserve exitstatus of a previous possible redirection
8865 * as POSIX mandates */
8866 return back_exitstatus;
8867}
Eric Andersenc470f442003-07-28 09:56:35 +00008868static void
8869evalcommand(union node *cmd, int flags)
8870{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008871 static const struct builtincmd null_bltin = {
8872 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008873 };
Eric Andersenc470f442003-07-28 09:56:35 +00008874 struct stackmark smark;
8875 union node *argp;
8876 struct arglist arglist;
8877 struct arglist varlist;
8878 char **argv;
8879 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008880 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008881 struct cmdentry cmdentry;
8882 struct job *jp;
8883 char *lastarg;
8884 const char *path;
8885 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008886 int status;
8887 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008888 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008889 smallint cmd_is_exec;
8890 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008891
8892 /* First expand the arguments. */
8893 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8894 setstackmark(&smark);
8895 back_exitstatus = 0;
8896
8897 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008898 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008899 varlist.lastp = &varlist.list;
8900 *varlist.lastp = NULL;
8901 arglist.lastp = &arglist.list;
8902 *arglist.lastp = NULL;
8903
8904 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008905 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008906 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8907 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8908 }
8909
Eric Andersenc470f442003-07-28 09:56:35 +00008910 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8911 struct strlist **spp;
8912
8913 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008914 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008915 expandarg(argp, &arglist, EXP_VARTILDE);
8916 else
8917 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8918
Eric Andersenc470f442003-07-28 09:56:35 +00008919 for (sp = *spp; sp; sp = sp->next)
8920 argc++;
8921 }
8922
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008923 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008924 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008925 TRACE(("evalcommand arg: %s\n", sp->text));
8926 *nargv++ = sp->text;
8927 }
8928 *nargv = NULL;
8929
8930 lastarg = NULL;
8931 if (iflag && funcnest == 0 && argc > 0)
8932 lastarg = nargv[-1];
8933
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008934 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008935 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008936 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008937
8938 path = vpath.text;
8939 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8940 struct strlist **spp;
8941 char *p;
8942
8943 spp = varlist.lastp;
8944 expandarg(argp, &varlist, EXP_VARTILDE);
8945
8946 /*
8947 * Modify the command lookup path, if a PATH= assignment
8948 * is present
8949 */
8950 p = (*spp)->text;
8951 if (varequal(p, path))
8952 path = p;
8953 }
8954
8955 /* Print the command if xflag is set. */
8956 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008957 int n;
8958 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008959
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008960 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008961 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008962
8963 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008964 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008965 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008966 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008967 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008968 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008969 p--;
8970 }
8971 }
8972 sp = arglist.list;
8973 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008974 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008975 }
8976
8977 cmd_is_exec = 0;
8978 spclbltin = -1;
8979
8980 /* Now locate the command. */
8981 if (argc) {
8982 const char *oldpath;
8983 int cmd_flag = DO_ERR;
8984
8985 path += 5;
8986 oldpath = path;
8987 for (;;) {
8988 find_command(argv[0], &cmdentry, cmd_flag, path);
8989 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008990 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00008991 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00008992 goto bail;
8993 }
8994
8995 /* implement bltin and command here */
8996 if (cmdentry.cmdtype != CMDBUILTIN)
8997 break;
8998 if (spclbltin < 0)
8999 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9000 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009001 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009002#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009003 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009004 path = oldpath;
9005 nargv = parse_command_args(argv, &path);
9006 if (!nargv)
9007 break;
9008 argc -= nargv - argv;
9009 argv = nargv;
9010 cmd_flag |= DO_NOFUNC;
9011 } else
9012#endif
9013 break;
9014 }
9015 }
9016
9017 if (status) {
9018 /* We have a redirection error. */
9019 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009020 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009021 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009022 exitstatus = status;
9023 goto out;
9024 }
9025
9026 /* Execute the command. */
9027 switch (cmdentry.cmdtype) {
9028 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009029
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009030#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009031/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
9032 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009033 {
9034 /* find_command() encodes applet_no as (-2 - applet_no) */
9035 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009036 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009037 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009038 /* run <applet>_main() */
9039 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009040 break;
9041 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009042 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009043#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009044 /* Fork off a child process if necessary. */
9045 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00009046 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009047 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009048 if (forkshell(jp, cmd, FORK_FG) != 0) {
9049 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009050 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009051 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009052 break;
9053 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009054 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009055 }
9056 listsetvar(varlist.list, VEXPORT|VSTACK);
9057 shellexec(argv, path, cmdentry.u.index);
9058 /* NOTREACHED */
9059
9060 case CMDBUILTIN:
9061 cmdenviron = varlist.list;
9062 if (cmdenviron) {
9063 struct strlist *list = cmdenviron;
9064 int i = VNOSET;
9065 if (spclbltin > 0 || argc == 0) {
9066 i = 0;
9067 if (cmd_is_exec && argc > 1)
9068 i = VEXPORT;
9069 }
9070 listsetvar(list, i);
9071 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009072 /* Tight loop with builtins only:
9073 * "while kill -0 $child; do true; done"
9074 * will never exit even if $child died, unless we do this
9075 * to reap the zombie and make kill detect that it's gone: */
9076 dowait(DOWAIT_NONBLOCK, NULL);
9077
Eric Andersenc470f442003-07-28 09:56:35 +00009078 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9079 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009080 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009081 if (i == EXEXIT)
9082 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009083 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009084 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009085 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009086 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009087 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009088 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009089 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009090 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009091 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009092 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009093 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009094 }
9095 break;
9096
9097 case CMDFUNCTION:
9098 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009099 /* See above for the rationale */
9100 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009101 if (evalfun(cmdentry.u.func, argc, argv, flags))
9102 goto raise;
9103 break;
9104 }
9105
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009106 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009107 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009108 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009109 /* dsl: I think this is intended to be used to support
9110 * '_' in 'vi' command mode during line editing...
9111 * However I implemented that within libedit itself.
9112 */
9113 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009114 }
Eric Andersenc470f442003-07-28 09:56:35 +00009115 popstackmark(&smark);
9116}
9117
9118static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009119evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9120{
Eric Andersenc470f442003-07-28 09:56:35 +00009121 char *volatile savecmdname;
9122 struct jmploc *volatile savehandler;
9123 struct jmploc jmploc;
9124 int i;
9125
9126 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009127 i = setjmp(jmploc.loc);
9128 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009129 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009130 savehandler = exception_handler;
9131 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009132 commandname = argv[0];
9133 argptr = argv + 1;
9134 optptr = NULL; /* initialize nextopt */
9135 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009136 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009137 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009138 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009139 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009140 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009141// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009142 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009143
9144 return i;
9145}
9146
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009147static int
9148goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009149{
9150 return !*endofname(p);
9151}
9152
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009153
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009154/*
9155 * Search for a command. This is called before we fork so that the
9156 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009157 * the child. The check for "goodname" is an overly conservative
9158 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009159 */
Eric Andersenc470f442003-07-28 09:56:35 +00009160static void
9161prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009162{
9163 struct cmdentry entry;
9164
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009165 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9166 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009167}
9168
Eric Andersencb57d552001-06-28 07:25:16 +00009169
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009170/* ============ Builtin commands
9171 *
9172 * Builtin commands whose functions are closely tied to evaluation
9173 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009174 */
9175
9176/*
Eric Andersencb57d552001-06-28 07:25:16 +00009177 * Handle break and continue commands. Break, continue, and return are
9178 * all handled by setting the evalskip flag. The evaluation routines
9179 * above all check this flag, and if it is set they start skipping
9180 * commands rather than executing them. The variable skipcount is
9181 * the number of loops to break/continue, or the number of function
9182 * levels to return. (The latter is always 1.) It should probably
9183 * be an error to break out of more loops than exist, but it isn't
9184 * in the standard shell so we don't make it one here.
9185 */
Eric Andersenc470f442003-07-28 09:56:35 +00009186static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009187breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009188{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009189 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009190
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009191 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009192 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009193 if (n > loopnest)
9194 n = loopnest;
9195 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009196 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009197 skipcount = n;
9198 }
9199 return 0;
9200}
9201
Eric Andersenc470f442003-07-28 09:56:35 +00009202
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009203/* ============ input.c
9204 *
Eric Andersen90898442003-08-06 11:20:52 +00009205 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009206 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009207
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009208enum {
9209 INPUT_PUSH_FILE = 1,
9210 INPUT_NOFILE_OK = 2,
9211};
Eric Andersencb57d552001-06-28 07:25:16 +00009212
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009213static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009214/* values of checkkwd variable */
9215#define CHKALIAS 0x1
9216#define CHKKWD 0x2
9217#define CHKNL 0x4
9218
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009219/*
9220 * Push a string back onto the input at this current parsefile level.
9221 * We handle aliases this way.
9222 */
9223#if !ENABLE_ASH_ALIAS
9224#define pushstring(s, ap) pushstring(s)
9225#endif
9226static void
9227pushstring(char *s, struct alias *ap)
9228{
9229 struct strpush *sp;
9230 int len;
9231
9232 len = strlen(s);
9233 INT_OFF;
9234 if (g_parsefile->strpush) {
9235 sp = ckzalloc(sizeof(*sp));
9236 sp->prev = g_parsefile->strpush;
9237 } else {
9238 sp = &(g_parsefile->basestrpush);
9239 }
9240 g_parsefile->strpush = sp;
9241 sp->prev_string = g_parsefile->next_to_pgetc;
9242 sp->prev_left_in_line = g_parsefile->left_in_line;
9243#if ENABLE_ASH_ALIAS
9244 sp->ap = ap;
9245 if (ap) {
9246 ap->flag |= ALIASINUSE;
9247 sp->string = s;
9248 }
9249#endif
9250 g_parsefile->next_to_pgetc = s;
9251 g_parsefile->left_in_line = len;
9252 INT_ON;
9253}
9254
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009255static void
9256popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009257{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009258 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009259
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009260 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009261#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009262 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009263 if (g_parsefile->next_to_pgetc[-1] == ' '
9264 || g_parsefile->next_to_pgetc[-1] == '\t'
9265 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009266 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009267 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009268 if (sp->string != sp->ap->val) {
9269 free(sp->string);
9270 }
9271 sp->ap->flag &= ~ALIASINUSE;
9272 if (sp->ap->flag & ALIASDEAD) {
9273 unalias(sp->ap->name);
9274 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009275 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009276#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009277 g_parsefile->next_to_pgetc = sp->prev_string;
9278 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009279 g_parsefile->strpush = sp->prev;
9280 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009281 free(sp);
9282 INT_ON;
9283}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009284
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009285//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9286//it peeks whether it is &>, and then pushes back both chars.
9287//This function needs to save last *next_to_pgetc to buf[0]
9288//to make two pungetc() reliable. Currently,
9289// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009290static int
9291preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009292{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009293 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009294 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009295
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009296 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009297#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009298 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009299 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009300 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009301 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009302#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009303 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009304#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009305 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9306 if (nr == 0) {
9307 /* Ctrl+C pressed */
9308 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009309 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009310 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009311 raise(SIGINT);
9312 return 1;
9313 }
Eric Andersenc470f442003-07-28 09:56:35 +00009314 goto retry;
9315 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009316 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009317 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009318 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009319 }
Eric Andersencb57d552001-06-28 07:25:16 +00009320 }
9321#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009322 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009323#endif
9324
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009325#if 0
9326/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009327 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009328 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009329 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009330 if (flags >= 0 && (flags & O_NONBLOCK)) {
9331 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009332 if (fcntl(0, F_SETFL, flags) >= 0) {
9333 out2str("sh: turning off NDELAY mode\n");
9334 goto retry;
9335 }
9336 }
9337 }
9338 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009339#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009340 return nr;
9341}
9342
9343/*
9344 * Refill the input buffer and return the next input character:
9345 *
9346 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009347 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9348 * or we are reading from a string so we can't refill the buffer,
9349 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009350 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9351 * 4) Process input up to the next newline, deleting nul characters.
9352 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009353//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9354#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009355/*
9356 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9357 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9358 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9359 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9360 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009361static int
Eric Andersenc470f442003-07-28 09:56:35 +00009362preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009363{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009364 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009365 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009366
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009367 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009368#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009369 if (g_parsefile->left_in_line == -1
9370 && g_parsefile->strpush->ap
9371 && g_parsefile->next_to_pgetc[-1] != ' '
9372 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009373 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009374 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009375 return PEOA;
9376 }
Eric Andersen2870d962001-07-02 17:27:21 +00009377#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009378 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009379 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009380 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9381 g_parsefile->left_in_line,
9382 g_parsefile->next_to_pgetc,
9383 g_parsefile->next_to_pgetc);
9384 if (--g_parsefile->left_in_line >= 0)
9385 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009386 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009387 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009388 * "pgetc" needs refilling.
9389 */
9390
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009391 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009392 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009393 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009394 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009395 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009396 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009397 /* even in failure keep left_in_line and next_to_pgetc
9398 * in lock step, for correct multi-layer pungetc.
9399 * left_in_line was decremented before preadbuffer(),
9400 * must inc next_to_pgetc: */
9401 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009402 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009403 }
Eric Andersencb57d552001-06-28 07:25:16 +00009404
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009405 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009406 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009407 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009408 again:
9409 more = preadfd();
9410 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009411 /* don't try reading again */
9412 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009413 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009414 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009415 return PEOF;
9416 }
9417 }
9418
Denis Vlasenko727752d2008-11-28 03:41:47 +00009419 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009420 * Set g_parsefile->left_in_line
9421 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009422 * NUL chars are deleted.
9423 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009424 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009425 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009426 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009427
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009428 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009429
Denis Vlasenko727752d2008-11-28 03:41:47 +00009430 c = *q;
9431 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009432 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009433 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009434 q++;
9435 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009436 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009437 break;
9438 }
Eric Andersencb57d552001-06-28 07:25:16 +00009439 }
9440
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009441 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009442 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9443 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009444 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009445 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009446 }
9447 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009448 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009449
Eric Andersencb57d552001-06-28 07:25:16 +00009450 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009451 char save = *q;
9452 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009453 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009454 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009455 }
9456
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009457 pgetc_debug("preadbuffer at %d:%p'%s'",
9458 g_parsefile->left_in_line,
9459 g_parsefile->next_to_pgetc,
9460 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009461 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009462}
9463
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009464#define pgetc_as_macro() \
9465 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009466 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009467 : preadbuffer() \
9468 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009469
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009470static int
9471pgetc(void)
9472{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009473 pgetc_debug("pgetc_fast at %d:%p'%s'",
9474 g_parsefile->left_in_line,
9475 g_parsefile->next_to_pgetc,
9476 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009477 return pgetc_as_macro();
9478}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009479
9480#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009481#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009482#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009483#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009484#endif
9485
9486/*
9487 * Same as pgetc(), but ignores PEOA.
9488 */
9489#if ENABLE_ASH_ALIAS
9490static int
9491pgetc2(void)
9492{
9493 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009494 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009495 pgetc_debug("pgetc_fast at %d:%p'%s'",
9496 g_parsefile->left_in_line,
9497 g_parsefile->next_to_pgetc,
9498 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009499 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009500 } while (c == PEOA);
9501 return c;
9502}
9503#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009504#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009505#endif
9506
9507/*
9508 * Read a line from the script.
9509 */
9510static char *
9511pfgets(char *line, int len)
9512{
9513 char *p = line;
9514 int nleft = len;
9515 int c;
9516
9517 while (--nleft > 0) {
9518 c = pgetc2();
9519 if (c == PEOF) {
9520 if (p == line)
9521 return NULL;
9522 break;
9523 }
9524 *p++ = c;
9525 if (c == '\n')
9526 break;
9527 }
9528 *p = '\0';
9529 return line;
9530}
9531
Eric Andersenc470f442003-07-28 09:56:35 +00009532/*
9533 * Undo the last call to pgetc. Only one character may be pushed back.
9534 * PEOF may be pushed back.
9535 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009536static void
Eric Andersenc470f442003-07-28 09:56:35 +00009537pungetc(void)
9538{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009539 g_parsefile->left_in_line++;
9540 g_parsefile->next_to_pgetc--;
9541 pgetc_debug("pushed back to %d:%p'%s'",
9542 g_parsefile->left_in_line,
9543 g_parsefile->next_to_pgetc,
9544 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009545}
9546
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009547/*
9548 * To handle the "." command, a stack of input files is used. Pushfile
9549 * adds a new entry to the stack and popfile restores the previous level.
9550 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009551static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009552pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009553{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009554 struct parsefile *pf;
9555
Denis Vlasenko597906c2008-02-20 16:38:54 +00009556 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009557 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009558 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009559 /*pf->strpush = NULL; - ckzalloc did it */
9560 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009561 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009562}
9563
9564static void
9565popfile(void)
9566{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009567 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009568
Denis Vlasenkob012b102007-02-19 22:43:01 +00009569 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009570 if (pf->fd >= 0)
9571 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009572 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009573 while (pf->strpush)
9574 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009575 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009576 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009577 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009578}
9579
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009580/*
9581 * Return to top level.
9582 */
9583static void
9584popallfiles(void)
9585{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009586 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009587 popfile();
9588}
9589
9590/*
9591 * Close the file(s) that the shell is reading commands from. Called
9592 * after a fork is done.
9593 */
9594static void
9595closescript(void)
9596{
9597 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009598 if (g_parsefile->fd > 0) {
9599 close(g_parsefile->fd);
9600 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009601 }
9602}
9603
9604/*
9605 * Like setinputfile, but takes an open file descriptor. Call this with
9606 * interrupts off.
9607 */
9608static void
9609setinputfd(int fd, int push)
9610{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009611 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009612 if (push) {
9613 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009614 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009615 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009616 g_parsefile->fd = fd;
9617 if (g_parsefile->buf == NULL)
9618 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009619 g_parsefile->left_in_buffer = 0;
9620 g_parsefile->left_in_line = 0;
9621 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009622}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009623
Eric Andersenc470f442003-07-28 09:56:35 +00009624/*
9625 * Set the input to take input from a file. If push is set, push the
9626 * old input onto the stack first.
9627 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009628static int
9629setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009630{
9631 int fd;
9632 int fd2;
9633
Denis Vlasenkob012b102007-02-19 22:43:01 +00009634 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009635 fd = open(fname, O_RDONLY);
9636 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009637 if (flags & INPUT_NOFILE_OK)
9638 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009639 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009640 }
Eric Andersenc470f442003-07-28 09:56:35 +00009641 if (fd < 10) {
9642 fd2 = copyfd(fd, 10);
9643 close(fd);
9644 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009645 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009646 fd = fd2;
9647 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009648 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009649 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009650 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009651 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009652}
9653
Eric Andersencb57d552001-06-28 07:25:16 +00009654/*
9655 * Like setinputfile, but takes input from a string.
9656 */
Eric Andersenc470f442003-07-28 09:56:35 +00009657static void
9658setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009659{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009660 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009661 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009662 g_parsefile->next_to_pgetc = string;
9663 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009664 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009665 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009666 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009667}
9668
9669
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009670/* ============ mail.c
9671 *
9672 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009673 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009674
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009675#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009676
Eric Andersencb57d552001-06-28 07:25:16 +00009677#define MAXMBOXES 10
9678
Eric Andersenc470f442003-07-28 09:56:35 +00009679/* times of mailboxes */
9680static time_t mailtime[MAXMBOXES];
9681/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009682static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009683
Eric Andersencb57d552001-06-28 07:25:16 +00009684/*
Eric Andersenc470f442003-07-28 09:56:35 +00009685 * Print appropriate message(s) if mail has arrived.
9686 * If mail_var_path_changed is set,
9687 * then the value of MAIL has mail_var_path_changed,
9688 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009689 */
Eric Andersenc470f442003-07-28 09:56:35 +00009690static void
9691chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009692{
Eric Andersencb57d552001-06-28 07:25:16 +00009693 const char *mpath;
9694 char *p;
9695 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009696 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009697 struct stackmark smark;
9698 struct stat statb;
9699
Eric Andersencb57d552001-06-28 07:25:16 +00009700 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009701 mpath = mpathset() ? mpathval() : mailval();
9702 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009703 p = padvance(&mpath, nullstr);
9704 if (p == NULL)
9705 break;
9706 if (*p == '\0')
9707 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009708 for (q = p; *q; q++)
9709 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009710#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009711 if (q[-1] != '/')
9712 abort();
9713#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009714 q[-1] = '\0'; /* delete trailing '/' */
9715 if (stat(p, &statb) < 0) {
9716 *mtp = 0;
9717 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009718 }
Eric Andersenc470f442003-07-28 09:56:35 +00009719 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9720 fprintf(
9721 stderr, snlfmt,
9722 pathopt ? pathopt : "you have mail"
9723 );
9724 }
9725 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009726 }
Eric Andersenc470f442003-07-28 09:56:35 +00009727 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009728 popstackmark(&smark);
9729}
Eric Andersencb57d552001-06-28 07:25:16 +00009730
Eric Andersenc470f442003-07-28 09:56:35 +00009731static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009732changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009733{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009734 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009735}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009736
Denis Vlasenko131ae172007-02-18 13:00:19 +00009737#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009738
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009739
9740/* ============ ??? */
9741
Eric Andersencb57d552001-06-28 07:25:16 +00009742/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009743 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009744 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009745static void
9746setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009747{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009748 char **newparam;
9749 char **ap;
9750 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009751
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009752 for (nparam = 0; argv[nparam]; nparam++)
9753 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009754 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9755 while (*argv) {
9756 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009757 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009758 *ap = NULL;
9759 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009760 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009761 shellparam.nparam = nparam;
9762 shellparam.p = newparam;
9763#if ENABLE_ASH_GETOPTS
9764 shellparam.optind = 1;
9765 shellparam.optoff = -1;
9766#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009767}
9768
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009769/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009770 * Process shell options. The global variable argptr contains a pointer
9771 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009772 *
9773 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9774 * For a non-interactive shell, an error condition encountered
9775 * by a special built-in ... shall cause the shell to write a diagnostic message
9776 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009777 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009778 * ...
9779 * Utility syntax error (option or operand error) Shall exit
9780 * ...
9781 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9782 * we see that bash does not do that (set "finishes" with error code 1 instead,
9783 * and shell continues), and people rely on this behavior!
9784 * Testcase:
9785 * set -o barfoo 2>/dev/null
9786 * echo $?
9787 *
9788 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009789 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009790static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009791plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009792{
9793 int i;
9794
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009795 if (name) {
9796 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009797 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009798 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009799 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009800 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009801 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009802 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009803 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009804 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009805 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009806 if (val) {
9807 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9808 } else {
9809 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9810 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009811 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009812 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009813}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009814static void
9815setoption(int flag, int val)
9816{
9817 int i;
9818
9819 for (i = 0; i < NOPTS; i++) {
9820 if (optletters(i) == flag) {
9821 optlist[i] = val;
9822 return;
9823 }
9824 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009825 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009826 /* NOTREACHED */
9827}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009828static int
Eric Andersenc470f442003-07-28 09:56:35 +00009829options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009830{
9831 char *p;
9832 int val;
9833 int c;
9834
9835 if (cmdline)
9836 minusc = NULL;
9837 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009838 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009839 if (c != '-' && c != '+')
9840 break;
9841 argptr++;
9842 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009843 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009844 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009845 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009846 if (!cmdline) {
9847 /* "-" means turn off -x and -v */
9848 if (p[0] == '\0')
9849 xflag = vflag = 0;
9850 /* "--" means reset params */
9851 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009852 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009853 }
Eric Andersenc470f442003-07-28 09:56:35 +00009854 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009855 }
Eric Andersencb57d552001-06-28 07:25:16 +00009856 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009857 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009858 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009859 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009860 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009861 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009862 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009863 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009864 /* it already printed err message */
9865 return 1; /* error */
9866 }
Eric Andersencb57d552001-06-28 07:25:16 +00009867 if (*argptr)
9868 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009869 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9870 isloginsh = 1;
9871 /* bash does not accept +-login, we also won't */
9872 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009873 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009874 isloginsh = 1;
9875 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009876 } else {
9877 setoption(c, val);
9878 }
9879 }
9880 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009881 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009882}
9883
Eric Andersencb57d552001-06-28 07:25:16 +00009884/*
Eric Andersencb57d552001-06-28 07:25:16 +00009885 * The shift builtin command.
9886 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009887static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009888shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009889{
9890 int n;
9891 char **ap1, **ap2;
9892
9893 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009894 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009895 n = number(argv[1]);
9896 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009897 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009898 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009899 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009900 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009901 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009902 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009903 }
9904 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009905 while ((*ap2++ = *ap1++) != NULL)
9906 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009907#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009908 shellparam.optind = 1;
9909 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009910#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009911 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009912 return 0;
9913}
9914
Eric Andersencb57d552001-06-28 07:25:16 +00009915/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009916 * POSIX requires that 'set' (but not export or readonly) output the
9917 * variables in lexicographic order - by the locale's collating order (sigh).
9918 * Maybe we could keep them in an ordered balanced binary tree
9919 * instead of hashed lists.
9920 * For now just roll 'em through qsort for printing...
9921 */
9922static int
9923showvars(const char *sep_prefix, int on, int off)
9924{
9925 const char *sep;
9926 char **ep, **epend;
9927
9928 ep = listvars(on, off, &epend);
9929 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9930
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009931 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009932
9933 for (; ep < epend; ep++) {
9934 const char *p;
9935 const char *q;
9936
9937 p = strchrnul(*ep, '=');
9938 q = nullstr;
9939 if (*p)
9940 q = single_quote(++p);
9941 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9942 }
9943 return 0;
9944}
9945
9946/*
Eric Andersencb57d552001-06-28 07:25:16 +00009947 * The set command builtin.
9948 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009949static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009950setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009951{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009952 int retval;
9953
Denis Vlasenko68404f12008-03-17 09:00:54 +00009954 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009955 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009956 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009957 retval = 1;
9958 if (!options(0)) { /* if no parse error... */
9959 retval = 0;
9960 optschanged();
9961 if (*argptr != NULL) {
9962 setparam(argptr);
9963 }
Eric Andersencb57d552001-06-28 07:25:16 +00009964 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009965 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009966 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009967}
9968
Denis Vlasenko131ae172007-02-18 13:00:19 +00009969#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009970static void
9971change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009972{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009973 /* Galois LFSR parameter */
9974 /* Taps at 32 31 29 1: */
9975 enum { MASK = 0x8000000b };
9976 /* Another example - taps at 32 31 30 10: */
9977 /* MASK = 0x00400007 */
9978
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009979 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009980 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009981 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009982
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009983 /* LCG has period of 2^32 and alternating lowest bit */
9984 random_LCG = 1664525 * random_LCG + 1013904223;
9985 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9986 t = (random_galois_LFSR << 1);
9987 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9988 t ^= MASK;
9989 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009990 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009991 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +00009992 * for $RANDOM range. Combining with subtraction is
9993 * just for fun. + and ^ would work equally well. */
9994 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +00009995 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009996 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +00009997 vrandom.flags &= ~VNOFUNC;
9998 } else {
9999 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +000010000 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +000010001 }
Eric Andersenef02f822004-03-11 13:34:24 +000010002}
Eric Andersen16767e22004-03-16 05:14:10 +000010003#endif
10004
Denis Vlasenko131ae172007-02-18 13:00:19 +000010005#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010006static int
Eric Andersenc470f442003-07-28 09:56:35 +000010007getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010008{
10009 char *p, *q;
10010 char c = '?';
10011 int done = 0;
10012 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010013 char s[12];
10014 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010015
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010016 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010017 return 1;
10018 optnext = optfirst + *param_optind - 1;
10019
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010020 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010021 p = NULL;
10022 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010023 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010024 if (p == NULL || *p == '\0') {
10025 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010026 p = *optnext;
10027 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010028 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010029 p = NULL;
10030 done = 1;
10031 goto out;
10032 }
10033 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010034 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010035 goto atend;
10036 }
10037
10038 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010039 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010040 if (*q == '\0') {
10041 if (optstr[0] == ':') {
10042 s[0] = c;
10043 s[1] = '\0';
10044 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010045 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010046 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010047 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010048 }
10049 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010050 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010051 }
10052 if (*++q == ':')
10053 q++;
10054 }
10055
10056 if (*++q == ':') {
10057 if (*p == '\0' && (p = *optnext) == NULL) {
10058 if (optstr[0] == ':') {
10059 s[0] = c;
10060 s[1] = '\0';
10061 err |= setvarsafe("OPTARG", s, 0);
10062 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010063 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010064 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010065 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010066 c = '?';
10067 }
Eric Andersenc470f442003-07-28 09:56:35 +000010068 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010069 }
10070
10071 if (p == *optnext)
10072 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010073 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010074 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010075 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010076 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010077 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010078 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010079 *param_optind = optnext - optfirst + 1;
10080 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010081 err |= setvarsafe("OPTIND", s, VNOFUNC);
10082 s[0] = c;
10083 s[1] = '\0';
10084 err |= setvarsafe(optvar, s, 0);
10085 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010086 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010087 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010088 flush_stdout_stderr();
10089 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010090 }
10091 return done;
10092}
Eric Andersenc470f442003-07-28 09:56:35 +000010093
10094/*
10095 * The getopts builtin. Shellparam.optnext points to the next argument
10096 * to be processed. Shellparam.optptr points to the next character to
10097 * be processed in the current argument. If shellparam.optnext is NULL,
10098 * then it's the first time getopts has been called.
10099 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010100static int
Eric Andersenc470f442003-07-28 09:56:35 +000010101getoptscmd(int argc, char **argv)
10102{
10103 char **optbase;
10104
10105 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010106 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010107 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010108 optbase = shellparam.p;
10109 if (shellparam.optind > shellparam.nparam + 1) {
10110 shellparam.optind = 1;
10111 shellparam.optoff = -1;
10112 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010113 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010114 optbase = &argv[3];
10115 if (shellparam.optind > argc - 2) {
10116 shellparam.optind = 1;
10117 shellparam.optoff = -1;
10118 }
10119 }
10120
10121 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010122 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010123}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010124#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010125
Eric Andersencb57d552001-06-28 07:25:16 +000010126
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010127/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010128
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010129struct heredoc {
10130 struct heredoc *next; /* next here document in list */
10131 union node *here; /* redirection node */
10132 char *eofmark; /* string indicating end of input */
10133 smallint striptabs; /* if set, strip leading tabs */
10134};
10135
10136static smallint tokpushback; /* last token pushed back */
10137static smallint parsebackquote; /* nonzero if we are inside backquotes */
10138static smallint quoteflag; /* set if (part of) last token was quoted */
10139static token_id_t lasttoken; /* last token read (integer id Txxx) */
10140static struct heredoc *heredoclist; /* list of here documents to read */
10141static char *wordtext; /* text of last word returned by readtoken */
10142static struct nodelist *backquotelist;
10143static union node *redirnode;
10144static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010145/*
10146 * NEOF is returned by parsecmd when it encounters an end of file. It
10147 * must be distinct from NULL, so we use the address of a variable that
10148 * happens to be handy.
10149 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010150#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010151
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010152static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010153static void
10154raise_error_syntax(const char *msg)
10155{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010156 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010157 /* NOTREACHED */
10158}
10159
10160/*
10161 * Called when an unexpected token is read during the parse. The argument
10162 * is the token that is expected, or -1 if more than one type of token can
10163 * occur at this point.
10164 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010165static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010166static void
10167raise_error_unexpected_syntax(int token)
10168{
10169 char msg[64];
10170 int l;
10171
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010172 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010173 if (token >= 0)
10174 sprintf(msg + l, " (expecting %s)", tokname(token));
10175 raise_error_syntax(msg);
10176 /* NOTREACHED */
10177}
Eric Andersencb57d552001-06-28 07:25:16 +000010178
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010179#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010180
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010181/* parsing is heavily cross-recursive, need these forward decls */
10182static union node *andor(void);
10183static union node *pipeline(void);
10184static union node *parse_command(void);
10185static void parseheredoc(void);
10186static char peektoken(void);
10187static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010188
Eric Andersenc470f442003-07-28 09:56:35 +000010189static union node *
10190list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010191{
10192 union node *n1, *n2, *n3;
10193 int tok;
10194
Eric Andersenc470f442003-07-28 09:56:35 +000010195 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10196 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010197 return NULL;
10198 n1 = NULL;
10199 for (;;) {
10200 n2 = andor();
10201 tok = readtoken();
10202 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010203 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010204 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010205 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010206 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010207 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010208 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010209 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010210 n2 = n3;
10211 }
10212 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010213 }
10214 }
10215 if (n1 == NULL) {
10216 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010217 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010218 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010219 n3->type = NSEMI;
10220 n3->nbinary.ch1 = n1;
10221 n3->nbinary.ch2 = n2;
10222 n1 = n3;
10223 }
10224 switch (tok) {
10225 case TBACKGND:
10226 case TSEMI:
10227 tok = readtoken();
10228 /* fall through */
10229 case TNL:
10230 if (tok == TNL) {
10231 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010232 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010233 return n1;
10234 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010235 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010236 }
Eric Andersenc470f442003-07-28 09:56:35 +000010237 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010238 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010239 return n1;
10240 break;
10241 case TEOF:
10242 if (heredoclist)
10243 parseheredoc();
10244 else
Eric Andersenc470f442003-07-28 09:56:35 +000010245 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010246 return n1;
10247 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010248 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010249 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010250 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010251 return n1;
10252 }
10253 }
10254}
10255
Eric Andersenc470f442003-07-28 09:56:35 +000010256static union node *
10257andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010258{
Eric Andersencb57d552001-06-28 07:25:16 +000010259 union node *n1, *n2, *n3;
10260 int t;
10261
Eric Andersencb57d552001-06-28 07:25:16 +000010262 n1 = pipeline();
10263 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010264 t = readtoken();
10265 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010266 t = NAND;
10267 } else if (t == TOR) {
10268 t = NOR;
10269 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010270 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010271 return n1;
10272 }
Eric Andersenc470f442003-07-28 09:56:35 +000010273 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010274 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010275 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010276 n3->type = t;
10277 n3->nbinary.ch1 = n1;
10278 n3->nbinary.ch2 = n2;
10279 n1 = n3;
10280 }
10281}
10282
Eric Andersenc470f442003-07-28 09:56:35 +000010283static union node *
10284pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010285{
Eric Andersencb57d552001-06-28 07:25:16 +000010286 union node *n1, *n2, *pipenode;
10287 struct nodelist *lp, *prev;
10288 int negate;
10289
10290 negate = 0;
10291 TRACE(("pipeline: entered\n"));
10292 if (readtoken() == TNOT) {
10293 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010294 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010295 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010296 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010297 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010298 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010299 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010300 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010301 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010302 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010303 pipenode->npipe.cmdlist = lp;
10304 lp->n = n1;
10305 do {
10306 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010307 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010308 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010309 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010310 prev->next = lp;
10311 } while (readtoken() == TPIPE);
10312 lp->next = NULL;
10313 n1 = pipenode;
10314 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010315 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010316 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010317 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010318 n2->type = NNOT;
10319 n2->nnot.com = n1;
10320 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010321 }
10322 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010323}
10324
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010325static union node *
10326makename(void)
10327{
10328 union node *n;
10329
Denis Vlasenko597906c2008-02-20 16:38:54 +000010330 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010331 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010332 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010333 n->narg.text = wordtext;
10334 n->narg.backquote = backquotelist;
10335 return n;
10336}
10337
10338static void
10339fixredir(union node *n, const char *text, int err)
10340{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010341 int fd;
10342
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010343 TRACE(("Fix redir %s %d\n", text, err));
10344 if (!err)
10345 n->ndup.vname = NULL;
10346
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010347 fd = bb_strtou(text, NULL, 10);
10348 if (!errno && fd >= 0)
10349 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010350 else if (LONE_DASH(text))
10351 n->ndup.dupfd = -1;
10352 else {
10353 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010354 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010355 n->ndup.vname = makename();
10356 }
10357}
10358
10359/*
10360 * Returns true if the text contains nothing to expand (no dollar signs
10361 * or backquotes).
10362 */
10363static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010364noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010365{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010366 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010367 char c;
10368
10369 p = text;
10370 while ((c = *p++) != '\0') {
10371 if (c == CTLQUOTEMARK)
10372 continue;
10373 if (c == CTLESC)
10374 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010375 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010376 return 0;
10377 }
10378 return 1;
10379}
10380
10381static void
10382parsefname(void)
10383{
10384 union node *n = redirnode;
10385
10386 if (readtoken() != TWORD)
10387 raise_error_unexpected_syntax(-1);
10388 if (n->type == NHERE) {
10389 struct heredoc *here = heredoc;
10390 struct heredoc *p;
10391 int i;
10392
10393 if (quoteflag == 0)
10394 n->type = NXHERE;
10395 TRACE(("Here document %d\n", n->type));
10396 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010397 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010398 rmescapes(wordtext);
10399 here->eofmark = wordtext;
10400 here->next = NULL;
10401 if (heredoclist == NULL)
10402 heredoclist = here;
10403 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010404 for (p = heredoclist; p->next; p = p->next)
10405 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010406 p->next = here;
10407 }
10408 } else if (n->type == NTOFD || n->type == NFROMFD) {
10409 fixredir(n, wordtext, 0);
10410 } else {
10411 n->nfile.fname = makename();
10412 }
10413}
Eric Andersencb57d552001-06-28 07:25:16 +000010414
Eric Andersenc470f442003-07-28 09:56:35 +000010415static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010416simplecmd(void)
10417{
10418 union node *args, **app;
10419 union node *n = NULL;
10420 union node *vars, **vpp;
10421 union node **rpp, *redir;
10422 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010423#if ENABLE_ASH_BASH_COMPAT
10424 smallint double_brackets_flag = 0;
10425#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010426
10427 args = NULL;
10428 app = &args;
10429 vars = NULL;
10430 vpp = &vars;
10431 redir = NULL;
10432 rpp = &redir;
10433
10434 savecheckkwd = CHKALIAS;
10435 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010436 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010437 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010438 t = readtoken();
10439 switch (t) {
10440#if ENABLE_ASH_BASH_COMPAT
10441 case TAND: /* "&&" */
10442 case TOR: /* "||" */
10443 if (!double_brackets_flag) {
10444 tokpushback = 1;
10445 goto out;
10446 }
10447 wordtext = (char *) (t == TAND ? "-a" : "-o");
10448#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010449 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010450 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010451 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010452 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010453 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010454#if ENABLE_ASH_BASH_COMPAT
10455 if (strcmp("[[", wordtext) == 0)
10456 double_brackets_flag = 1;
10457 else if (strcmp("]]", wordtext) == 0)
10458 double_brackets_flag = 0;
10459#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010460 n->narg.backquote = backquotelist;
10461 if (savecheckkwd && isassignment(wordtext)) {
10462 *vpp = n;
10463 vpp = &n->narg.next;
10464 } else {
10465 *app = n;
10466 app = &n->narg.next;
10467 savecheckkwd = 0;
10468 }
10469 break;
10470 case TREDIR:
10471 *rpp = n = redirnode;
10472 rpp = &n->nfile.next;
10473 parsefname(); /* read name of redirection file */
10474 break;
10475 case TLP:
10476 if (args && app == &args->narg.next
10477 && !vars && !redir
10478 ) {
10479 struct builtincmd *bcmd;
10480 const char *name;
10481
10482 /* We have a function */
10483 if (readtoken() != TRP)
10484 raise_error_unexpected_syntax(TRP);
10485 name = n->narg.text;
10486 if (!goodname(name)
10487 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10488 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010489 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010490 }
10491 n->type = NDEFUN;
10492 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10493 n->narg.next = parse_command();
10494 return n;
10495 }
10496 /* fall through */
10497 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010498 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010499 goto out;
10500 }
10501 }
10502 out:
10503 *app = NULL;
10504 *vpp = NULL;
10505 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010506 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010507 n->type = NCMD;
10508 n->ncmd.args = args;
10509 n->ncmd.assign = vars;
10510 n->ncmd.redirect = redir;
10511 return n;
10512}
10513
10514static union node *
10515parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010516{
Eric Andersencb57d552001-06-28 07:25:16 +000010517 union node *n1, *n2;
10518 union node *ap, **app;
10519 union node *cp, **cpp;
10520 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010521 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010522 int t;
10523
10524 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010525 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010526
Eric Andersencb57d552001-06-28 07:25:16 +000010527 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010528 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010529 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010530 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010531 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010532 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010533 n1->type = NIF;
10534 n1->nif.test = list(0);
10535 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010536 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010537 n1->nif.ifpart = list(0);
10538 n2 = n1;
10539 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010540 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010541 n2 = n2->nif.elsepart;
10542 n2->type = NIF;
10543 n2->nif.test = list(0);
10544 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010545 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010546 n2->nif.ifpart = list(0);
10547 }
10548 if (lasttoken == TELSE)
10549 n2->nif.elsepart = list(0);
10550 else {
10551 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010552 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010553 }
Eric Andersenc470f442003-07-28 09:56:35 +000010554 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010555 break;
10556 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010557 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010558 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010559 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010560 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010561 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010562 got = readtoken();
10563 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010564 TRACE(("expecting DO got %s %s\n", tokname(got),
10565 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010566 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010567 }
10568 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010569 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010570 break;
10571 }
10572 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010573 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010574 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010575 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010576 n1->type = NFOR;
10577 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010578 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010579 if (readtoken() == TIN) {
10580 app = &ap;
10581 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010582 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010583 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010584 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010585 n2->narg.text = wordtext;
10586 n2->narg.backquote = backquotelist;
10587 *app = n2;
10588 app = &n2->narg.next;
10589 }
10590 *app = NULL;
10591 n1->nfor.args = ap;
10592 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010593 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010594 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010595 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010596 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010597 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010598 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010599 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010600 n1->nfor.args = n2;
10601 /*
10602 * Newline or semicolon here is optional (but note
10603 * that the original Bourne shell only allowed NL).
10604 */
10605 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010606 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010607 }
Eric Andersenc470f442003-07-28 09:56:35 +000010608 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010609 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010610 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010611 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010612 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010613 break;
10614 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010615 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010616 n1->type = NCASE;
10617 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010618 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010619 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010620 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010621 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010622 n2->narg.text = wordtext;
10623 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010624 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010625 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010626 } while (readtoken() == TNL);
10627 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010628 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010629 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010630 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010631 checkkwd = CHKNL | CHKKWD;
10632 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010633 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010634 if (lasttoken == TLP)
10635 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010636 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010637 cp->type = NCLIST;
10638 app = &cp->nclist.pattern;
10639 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010640 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010641 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010642 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010643 ap->narg.text = wordtext;
10644 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010645 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010646 break;
10647 app = &ap->narg.next;
10648 readtoken();
10649 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010650 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010651 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010652 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010653 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010654
Eric Andersenc470f442003-07-28 09:56:35 +000010655 cpp = &cp->nclist.next;
10656
10657 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010658 t = readtoken();
10659 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010660 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010661 raise_error_unexpected_syntax(TENDCASE);
10662 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010663 }
Eric Andersenc470f442003-07-28 09:56:35 +000010664 }
Eric Andersencb57d552001-06-28 07:25:16 +000010665 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010666 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010667 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010668 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010669 n1->type = NSUBSHELL;
10670 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010671 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010672 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010673 break;
10674 case TBEGIN:
10675 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010676 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010677 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010678 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010679 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010680 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010681 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010682 }
10683
Eric Andersenc470f442003-07-28 09:56:35 +000010684 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010685 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010686
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010687 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010688 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010689 checkkwd = CHKKWD | CHKALIAS;
10690 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010691 while (readtoken() == TREDIR) {
10692 *rpp = n2 = redirnode;
10693 rpp = &n2->nfile.next;
10694 parsefname();
10695 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010696 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010697 *rpp = NULL;
10698 if (redir) {
10699 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010700 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010701 n2->type = NREDIR;
10702 n2->nredir.n = n1;
10703 n1 = n2;
10704 }
10705 n1->nredir.redirect = redir;
10706 }
Eric Andersencb57d552001-06-28 07:25:16 +000010707 return n1;
10708}
10709
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010710#if ENABLE_ASH_BASH_COMPAT
10711static int decode_dollar_squote(void)
10712{
10713 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10714 int c, cnt;
10715 char *p;
10716 char buf[4];
10717
10718 c = pgetc();
10719 p = strchr(C_escapes, c);
10720 if (p) {
10721 buf[0] = c;
10722 p = buf;
10723 cnt = 3;
10724 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10725 do {
10726 c = pgetc();
10727 *++p = c;
10728 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10729 pungetc();
10730 } else if (c == 'x') { /* \xHH */
10731 do {
10732 c = pgetc();
10733 *++p = c;
10734 } while (isxdigit(c) && --cnt);
10735 pungetc();
10736 if (cnt == 3) { /* \x but next char is "bad" */
10737 c = 'x';
10738 goto unrecognized;
10739 }
10740 } else { /* simple seq like \\ or \t */
10741 p++;
10742 }
10743 *p = '\0';
10744 p = buf;
10745 c = bb_process_escape_sequence((void*)&p);
10746 } else { /* unrecognized "\z": print both chars unless ' or " */
10747 if (c != '\'' && c != '"') {
10748 unrecognized:
10749 c |= 0x100; /* "please encode \, then me" */
10750 }
10751 }
10752 return c;
10753}
10754#endif
10755
Eric Andersencb57d552001-06-28 07:25:16 +000010756/*
10757 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10758 * is not NULL, read a here document. In the latter case, eofmark is the
10759 * word which marks the end of the document and striptabs is true if
10760 * leading tabs should be stripped from the document. The argument firstc
10761 * is the first character of the input token or document.
10762 *
10763 * Because C does not have internal subroutines, I have simulated them
10764 * using goto's to implement the subroutine linkage. The following macros
10765 * will run code that appears at the end of readtoken1.
10766 */
Eric Andersen2870d962001-07-02 17:27:21 +000010767#define CHECKEND() {goto checkend; checkend_return:;}
10768#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10769#define PARSESUB() {goto parsesub; parsesub_return:;}
10770#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10771#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10772#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010773static int
Eric Andersenc470f442003-07-28 09:56:35 +000010774readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010775{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010776 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010777 int c = firstc;
10778 char *out;
10779 int len;
10780 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010781 struct nodelist *bqlist;
10782 smallint quotef;
10783 smallint dblquote;
10784 smallint oldstyle;
10785 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010786#if ENABLE_ASH_EXPAND_PRMT
10787 smallint pssyntax; /* we are expanding a prompt string */
10788#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010789 int varnest; /* levels of variables expansion */
10790 int arinest; /* levels of arithmetic expansion */
10791 int parenlevel; /* levels of parens in arithmetic */
10792 int dqvarnest; /* levels of variables expansion within double quotes */
10793
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010794 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10795
Eric Andersencb57d552001-06-28 07:25:16 +000010796#if __GNUC__
10797 /* Avoid longjmp clobbering */
10798 (void) &out;
10799 (void) &quotef;
10800 (void) &dblquote;
10801 (void) &varnest;
10802 (void) &arinest;
10803 (void) &parenlevel;
10804 (void) &dqvarnest;
10805 (void) &oldstyle;
10806 (void) &prevsyntax;
10807 (void) &syntax;
10808#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010809 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010810 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010811 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010812 oldstyle = 0;
10813 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010814#if ENABLE_ASH_EXPAND_PRMT
10815 pssyntax = (syntax == PSSYNTAX);
10816 if (pssyntax)
10817 syntax = DQSYNTAX;
10818#endif
10819 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010820 varnest = 0;
10821 arinest = 0;
10822 parenlevel = 0;
10823 dqvarnest = 0;
10824
10825 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010826 loop:
10827 /* For each line, until end of word */
10828 {
Eric Andersenc470f442003-07-28 09:56:35 +000010829 CHECKEND(); /* set c to PEOF if at end of here document */
10830 for (;;) { /* until end of line or end of word */
10831 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010832 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010833 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010834 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010835 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010836 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010837 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010838 if (doprompt)
10839 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010840 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010841 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010842 case CWORD:
10843 USTPUTC(c, out);
10844 break;
10845 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010846 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010847 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010848#if ENABLE_ASH_BASH_COMPAT
10849 if (c == '\\' && bash_dollar_squote) {
10850 c = decode_dollar_squote();
10851 if (c & 0x100) {
10852 USTPUTC('\\', out);
10853 c = (unsigned char)c;
10854 }
10855 }
10856#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010857 USTPUTC(c, out);
10858 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010859 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010860 c = pgetc2();
10861 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010862 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010863 USTPUTC('\\', out);
10864 pungetc();
10865 } else if (c == '\n') {
10866 if (doprompt)
10867 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010868 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010869#if ENABLE_ASH_EXPAND_PRMT
10870 if (c == '$' && pssyntax) {
10871 USTPUTC(CTLESC, out);
10872 USTPUTC('\\', out);
10873 }
10874#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010875 if (dblquote && c != '\\'
10876 && c != '`' && c != '$'
10877 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010878 ) {
10879 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010880 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010881 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010882 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010883 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010884 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010885 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010886 }
10887 break;
10888 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010889 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010890 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010891 if (eofmark == NULL) {
10892 USTPUTC(CTLQUOTEMARK, out);
10893 }
Eric Andersencb57d552001-06-28 07:25:16 +000010894 break;
10895 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010896 syntax = DQSYNTAX;
10897 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010898 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010899 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010900 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010901 if (eofmark != NULL && arinest == 0
10902 && varnest == 0
10903 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010904 USTPUTC(c, out);
10905 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010906 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010907 syntax = BASESYNTAX;
10908 dblquote = 0;
10909 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010910 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010911 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010912 }
10913 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010914 case CVAR: /* '$' */
10915 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010916 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010917 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010918 if (varnest > 0) {
10919 varnest--;
10920 if (dqvarnest > 0) {
10921 dqvarnest--;
10922 }
10923 USTPUTC(CTLENDVAR, out);
10924 } else {
10925 USTPUTC(c, out);
10926 }
10927 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010928#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010929 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010930 parenlevel++;
10931 USTPUTC(c, out);
10932 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010933 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010934 if (parenlevel > 0) {
10935 USTPUTC(c, out);
10936 --parenlevel;
10937 } else {
10938 if (pgetc() == ')') {
10939 if (--arinest == 0) {
10940 USTPUTC(CTLENDARI, out);
10941 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010942 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010943 } else
10944 USTPUTC(')', out);
10945 } else {
10946 /*
10947 * unbalanced parens
10948 * (don't 2nd guess - no error)
10949 */
10950 pungetc();
10951 USTPUTC(')', out);
10952 }
10953 }
10954 break;
10955#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010956 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010957 PARSEBACKQOLD();
10958 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010959 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010960 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010961 case CIGN:
10962 break;
10963 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010964 if (varnest == 0) {
10965#if ENABLE_ASH_BASH_COMPAT
10966 if (c == '&') {
10967 if (pgetc() == '>')
10968 c = 0x100 + '>'; /* flag &> */
10969 pungetc();
10970 }
10971#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010972 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010973 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010974#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010975 if (c != PEOA)
10976#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010977 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010978
Eric Andersencb57d552001-06-28 07:25:16 +000010979 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010980 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010981 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010982 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010983 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010984#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010985 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010986 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010987#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010988 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010989 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000010990 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010991 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000010992 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000010993 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000010994 }
10995 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010996 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000010997 out = stackblock();
10998 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000010999 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
11000 && quotef == 0
11001 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011002 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011003 PARSEREDIR(); /* passed as params: out, c */
11004 lasttoken = TREDIR;
11005 return lasttoken;
11006 }
11007 /* else: non-number X seen, interpret it
11008 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011009 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011010 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011011 }
11012 quoteflag = quotef;
11013 backquotelist = bqlist;
11014 grabstackblock(len);
11015 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011016 lasttoken = TWORD;
11017 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011018/* end of readtoken routine */
11019
Eric Andersencb57d552001-06-28 07:25:16 +000011020/*
11021 * Check to see whether we are at the end of the here document. When this
11022 * is called, c is set to the first character of the next input line. If
11023 * we are at the end of the here document, this routine sets the c to PEOF.
11024 */
Eric Andersenc470f442003-07-28 09:56:35 +000011025checkend: {
11026 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011027#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011028 if (c == PEOA) {
11029 c = pgetc2();
11030 }
11031#endif
11032 if (striptabs) {
11033 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000011034 c = pgetc2();
11035 }
Eric Andersenc470f442003-07-28 09:56:35 +000011036 }
11037 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011038 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011039 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011040
Eric Andersenc470f442003-07-28 09:56:35 +000011041 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011042 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11043 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011044 if (*p == '\n' && *q == '\0') {
11045 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011046 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011047 needprompt = doprompt;
11048 } else {
11049 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011050 }
11051 }
11052 }
11053 }
Eric Andersenc470f442003-07-28 09:56:35 +000011054 goto checkend_return;
11055}
Eric Andersencb57d552001-06-28 07:25:16 +000011056
Eric Andersencb57d552001-06-28 07:25:16 +000011057/*
11058 * Parse a redirection operator. The variable "out" points to a string
11059 * specifying the fd to be redirected. The variable "c" contains the
11060 * first character of the redirection operator.
11061 */
Eric Andersenc470f442003-07-28 09:56:35 +000011062parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011063 /* out is already checked to be a valid number or "" */
11064 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011065 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011066
Denis Vlasenko597906c2008-02-20 16:38:54 +000011067 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011068 if (c == '>') {
11069 np->nfile.fd = 1;
11070 c = pgetc();
11071 if (c == '>')
11072 np->type = NAPPEND;
11073 else if (c == '|')
11074 np->type = NCLOBBER;
11075 else if (c == '&')
11076 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011077 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011078 else {
11079 np->type = NTO;
11080 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011081 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011082 }
11083#if ENABLE_ASH_BASH_COMPAT
11084 else if (c == 0x100 + '>') { /* this flags &> redirection */
11085 np->nfile.fd = 1;
11086 pgetc(); /* this is '>', no need to check */
11087 np->type = NTO2;
11088 }
11089#endif
11090 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011091 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011092 c = pgetc();
11093 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011094 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011095 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011096 np = stzalloc(sizeof(struct nhere));
11097 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011098 }
11099 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011100 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011101 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011102 c = pgetc();
11103 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011104 heredoc->striptabs = 1;
11105 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011106 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011107 pungetc();
11108 }
11109 break;
11110
11111 case '&':
11112 np->type = NFROMFD;
11113 break;
11114
11115 case '>':
11116 np->type = NFROMTO;
11117 break;
11118
11119 default:
11120 np->type = NFROM;
11121 pungetc();
11122 break;
11123 }
Eric Andersencb57d552001-06-28 07:25:16 +000011124 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011125 if (fd >= 0)
11126 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011127 redirnode = np;
11128 goto parseredir_return;
11129}
Eric Andersencb57d552001-06-28 07:25:16 +000011130
Eric Andersencb57d552001-06-28 07:25:16 +000011131/*
11132 * Parse a substitution. At this point, we have read the dollar sign
11133 * and nothing else.
11134 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011135
11136/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11137 * (assuming ascii char codes, as the original implementation did) */
11138#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011139 (((unsigned)(c) - 33 < 32) \
11140 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011141parsesub: {
11142 int subtype;
11143 int typeloc;
11144 int flags;
11145 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011146 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011147
Eric Andersenc470f442003-07-28 09:56:35 +000011148 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011149 if (c <= PEOA_OR_PEOF
11150 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011151 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011152#if ENABLE_ASH_BASH_COMPAT
11153 if (c == '\'')
11154 bash_dollar_squote = 1;
11155 else
11156#endif
11157 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011158 pungetc();
11159 } else if (c == '(') { /* $(command) or $((arith)) */
11160 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011161#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011162 PARSEARITH();
11163#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011164 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011165#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011166 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011167 pungetc();
11168 PARSEBACKQNEW();
11169 }
11170 } else {
11171 USTPUTC(CTLVAR, out);
11172 typeloc = out - (char *)stackblock();
11173 USTPUTC(VSNORMAL, out);
11174 subtype = VSNORMAL;
11175 if (c == '{') {
11176 c = pgetc();
11177 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011178 c = pgetc();
11179 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011180 c = '#';
11181 else
11182 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011183 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011184 subtype = 0;
11185 }
11186 if (c > PEOA_OR_PEOF && is_name(c)) {
11187 do {
11188 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011189 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011190 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011191 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011192 do {
11193 STPUTC(c, out);
11194 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011195 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011196 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011197 USTPUTC(c, out);
11198 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011199 } else {
11200 badsub:
11201 raise_error_syntax("bad substitution");
11202 }
Eric Andersencb57d552001-06-28 07:25:16 +000011203
Eric Andersenc470f442003-07-28 09:56:35 +000011204 STPUTC('=', out);
11205 flags = 0;
11206 if (subtype == 0) {
11207 switch (c) {
11208 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011209 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011210#if ENABLE_ASH_BASH_COMPAT
11211 if (c == ':' || c == '$' || isdigit(c)) {
11212 pungetc();
11213 subtype = VSSUBSTR;
11214 break;
11215 }
11216#endif
11217 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011218 /*FALLTHROUGH*/
11219 default:
11220 p = strchr(types, c);
11221 if (p == NULL)
11222 goto badsub;
11223 subtype = p - types + VSNORMAL;
11224 break;
11225 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011226 case '#': {
11227 int cc = c;
11228 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11229 c = pgetc();
11230 if (c == cc)
11231 subtype++;
11232 else
11233 pungetc();
11234 break;
11235 }
11236#if ENABLE_ASH_BASH_COMPAT
11237 case '/':
11238 subtype = VSREPLACE;
11239 c = pgetc();
11240 if (c == '/')
11241 subtype++; /* VSREPLACEALL */
11242 else
11243 pungetc();
11244 break;
11245#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011246 }
Eric Andersenc470f442003-07-28 09:56:35 +000011247 } else {
11248 pungetc();
11249 }
11250 if (dblquote || arinest)
11251 flags |= VSQUOTE;
11252 *((char *)stackblock() + typeloc) = subtype | flags;
11253 if (subtype != VSNORMAL) {
11254 varnest++;
11255 if (dblquote || arinest) {
11256 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011257 }
11258 }
11259 }
Eric Andersenc470f442003-07-28 09:56:35 +000011260 goto parsesub_return;
11261}
Eric Andersencb57d552001-06-28 07:25:16 +000011262
Eric Andersencb57d552001-06-28 07:25:16 +000011263/*
11264 * Called to parse command substitutions. Newstyle is set if the command
11265 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11266 * list of commands (passed by reference), and savelen is the number of
11267 * characters on the top of the stack which must be preserved.
11268 */
Eric Andersenc470f442003-07-28 09:56:35 +000011269parsebackq: {
11270 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011271 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011272 union node *n;
11273 char *volatile str;
11274 struct jmploc jmploc;
11275 struct jmploc *volatile savehandler;
11276 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011277 smallint saveprompt = 0;
11278
Eric Andersencb57d552001-06-28 07:25:16 +000011279#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011280 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011281#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011282 savepbq = parsebackquote;
11283 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011284 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011285 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011286 exception_handler = savehandler;
11287 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011288 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011289 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011290 str = NULL;
11291 savelen = out - (char *)stackblock();
11292 if (savelen > 0) {
11293 str = ckmalloc(savelen);
11294 memcpy(str, stackblock(), savelen);
11295 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011296 savehandler = exception_handler;
11297 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011298 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011299 if (oldstyle) {
11300 /* We must read until the closing backquote, giving special
11301 treatment to some slashes, and then push the string and
11302 reread it as input, interpreting it normally. */
11303 char *pout;
11304 int pc;
11305 size_t psavelen;
11306 char *pstr;
11307
11308
11309 STARTSTACKSTR(pout);
11310 for (;;) {
11311 if (needprompt) {
11312 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011313 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011314 pc = pgetc();
11315 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011316 case '`':
11317 goto done;
11318
11319 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011320 pc = pgetc();
11321 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011322 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011323 if (doprompt)
11324 setprompt(2);
11325 /*
11326 * If eating a newline, avoid putting
11327 * the newline into the new character
11328 * stream (via the STPUTC after the
11329 * switch).
11330 */
11331 continue;
11332 }
11333 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011334 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011335 STPUTC('\\', pout);
11336 if (pc > PEOA_OR_PEOF) {
11337 break;
11338 }
11339 /* fall through */
11340
11341 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011342#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011343 case PEOA:
11344#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011345 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011346 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011347
11348 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011349 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011350 needprompt = doprompt;
11351 break;
11352
11353 default:
11354 break;
11355 }
11356 STPUTC(pc, pout);
11357 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011358 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011359 STPUTC('\0', pout);
11360 psavelen = pout - (char *)stackblock();
11361 if (psavelen > 0) {
11362 pstr = grabstackstr(pout);
11363 setinputstring(pstr);
11364 }
11365 }
11366 nlpp = &bqlist;
11367 while (*nlpp)
11368 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011369 *nlpp = stzalloc(sizeof(**nlpp));
11370 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011371 parsebackquote = oldstyle;
11372
11373 if (oldstyle) {
11374 saveprompt = doprompt;
11375 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011376 }
11377
Eric Andersenc470f442003-07-28 09:56:35 +000011378 n = list(2);
11379
11380 if (oldstyle)
11381 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011382 else if (readtoken() != TRP)
11383 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011384
11385 (*nlpp)->n = n;
11386 if (oldstyle) {
11387 /*
11388 * Start reading from old file again, ignoring any pushed back
11389 * tokens left from the backquote parsing
11390 */
11391 popfile();
11392 tokpushback = 0;
11393 }
11394 while (stackblocksize() <= savelen)
11395 growstackblock();
11396 STARTSTACKSTR(out);
11397 if (str) {
11398 memcpy(out, str, savelen);
11399 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011400 INT_OFF;
11401 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011402 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011403 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011404 }
11405 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011406 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011407 if (arinest || dblquote)
11408 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11409 else
11410 USTPUTC(CTLBACKQ, out);
11411 if (oldstyle)
11412 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011413 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011414}
11415
Denis Vlasenko131ae172007-02-18 13:00:19 +000011416#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011417/*
11418 * Parse an arithmetic expansion (indicate start of one and set state)
11419 */
Eric Andersenc470f442003-07-28 09:56:35 +000011420parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011421 if (++arinest == 1) {
11422 prevsyntax = syntax;
11423 syntax = ARISYNTAX;
11424 USTPUTC(CTLARI, out);
11425 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011426 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011427 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011428 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011429 } else {
11430 /*
11431 * we collapse embedded arithmetic expansion to
11432 * parenthesis, which should be equivalent
11433 */
11434 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011435 }
Eric Andersenc470f442003-07-28 09:56:35 +000011436 goto parsearith_return;
11437}
11438#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011439
Eric Andersenc470f442003-07-28 09:56:35 +000011440} /* end of readtoken */
11441
Eric Andersencb57d552001-06-28 07:25:16 +000011442/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011443 * Read the next input token.
11444 * If the token is a word, we set backquotelist to the list of cmds in
11445 * backquotes. We set quoteflag to true if any part of the word was
11446 * quoted.
11447 * If the token is TREDIR, then we set redirnode to a structure containing
11448 * the redirection.
11449 * In all cases, the variable startlinno is set to the number of the line
11450 * on which the token starts.
11451 *
11452 * [Change comment: here documents and internal procedures]
11453 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11454 * word parsing code into a separate routine. In this case, readtoken
11455 * doesn't need to have any internal procedures, but parseword does.
11456 * We could also make parseoperator in essence the main routine, and
11457 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011458 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011459#define NEW_xxreadtoken
11460#ifdef NEW_xxreadtoken
11461/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011462static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011463 '\n', '(', ')', /* singles */
11464 '&', '|', ';', /* doubles */
11465 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011466};
Eric Andersencb57d552001-06-28 07:25:16 +000011467
Denis Vlasenko834dee72008-10-07 09:18:30 +000011468#define xxreadtoken_singles 3
11469#define xxreadtoken_doubles 3
11470
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011471static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011472 TNL, TLP, TRP, /* only single occurrence allowed */
11473 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11474 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011475 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011476};
11477
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011478static int
11479xxreadtoken(void)
11480{
11481 int c;
11482
11483 if (tokpushback) {
11484 tokpushback = 0;
11485 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011486 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011487 if (needprompt) {
11488 setprompt(2);
11489 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011490 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011491 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011492 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011493 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11494 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011495
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011496 if (c == '#') {
11497 while ((c = pgetc()) != '\n' && c != PEOF)
11498 continue;
11499 pungetc();
11500 } else if (c == '\\') {
11501 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011502 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011503 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011504 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011505 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011506 if (doprompt)
11507 setprompt(2);
11508 } else {
11509 const char *p;
11510
11511 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11512 if (c != PEOF) {
11513 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011514 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011515 needprompt = doprompt;
11516 }
11517
11518 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011519 if (p == NULL)
11520 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011521
Denis Vlasenko834dee72008-10-07 09:18:30 +000011522 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11523 int cc = pgetc();
11524 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011525 p += xxreadtoken_doubles + 1;
11526 } else {
11527 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011528#if ENABLE_ASH_BASH_COMPAT
11529 if (c == '&' && cc == '>') /* &> */
11530 break; /* return readtoken1(...) */
11531#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011532 }
11533 }
11534 }
11535 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11536 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011537 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011538 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011539
11540 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011541}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011542#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011543#define RETURN(token) return lasttoken = token
11544static int
11545xxreadtoken(void)
11546{
11547 int c;
11548
11549 if (tokpushback) {
11550 tokpushback = 0;
11551 return lasttoken;
11552 }
11553 if (needprompt) {
11554 setprompt(2);
11555 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011556 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011557 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011558 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011559 switch (c) {
11560 case ' ': case '\t':
11561#if ENABLE_ASH_ALIAS
11562 case PEOA:
11563#endif
11564 continue;
11565 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011566 while ((c = pgetc()) != '\n' && c != PEOF)
11567 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011568 pungetc();
11569 continue;
11570 case '\\':
11571 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011572 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011573 if (doprompt)
11574 setprompt(2);
11575 continue;
11576 }
11577 pungetc();
11578 goto breakloop;
11579 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011580 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011581 needprompt = doprompt;
11582 RETURN(TNL);
11583 case PEOF:
11584 RETURN(TEOF);
11585 case '&':
11586 if (pgetc() == '&')
11587 RETURN(TAND);
11588 pungetc();
11589 RETURN(TBACKGND);
11590 case '|':
11591 if (pgetc() == '|')
11592 RETURN(TOR);
11593 pungetc();
11594 RETURN(TPIPE);
11595 case ';':
11596 if (pgetc() == ';')
11597 RETURN(TENDCASE);
11598 pungetc();
11599 RETURN(TSEMI);
11600 case '(':
11601 RETURN(TLP);
11602 case ')':
11603 RETURN(TRP);
11604 default:
11605 goto breakloop;
11606 }
11607 }
11608 breakloop:
11609 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11610#undef RETURN
11611}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011612#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011613
11614static int
11615readtoken(void)
11616{
11617 int t;
11618#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011619 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011620#endif
11621
11622#if ENABLE_ASH_ALIAS
11623 top:
11624#endif
11625
11626 t = xxreadtoken();
11627
11628 /*
11629 * eat newlines
11630 */
11631 if (checkkwd & CHKNL) {
11632 while (t == TNL) {
11633 parseheredoc();
11634 t = xxreadtoken();
11635 }
11636 }
11637
11638 if (t != TWORD || quoteflag) {
11639 goto out;
11640 }
11641
11642 /*
11643 * check for keywords
11644 */
11645 if (checkkwd & CHKKWD) {
11646 const char *const *pp;
11647
11648 pp = findkwd(wordtext);
11649 if (pp) {
11650 lasttoken = t = pp - tokname_array;
11651 TRACE(("keyword %s recognized\n", tokname(t)));
11652 goto out;
11653 }
11654 }
11655
11656 if (checkkwd & CHKALIAS) {
11657#if ENABLE_ASH_ALIAS
11658 struct alias *ap;
11659 ap = lookupalias(wordtext, 1);
11660 if (ap != NULL) {
11661 if (*ap->val) {
11662 pushstring(ap->val, ap);
11663 }
11664 goto top;
11665 }
11666#endif
11667 }
11668 out:
11669 checkkwd = 0;
11670#if DEBUG
11671 if (!alreadyseen)
11672 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11673 else
11674 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11675#endif
11676 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011677}
11678
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011679static char
11680peektoken(void)
11681{
11682 int t;
11683
11684 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011685 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011686 return tokname_array[t][0];
11687}
Eric Andersencb57d552001-06-28 07:25:16 +000011688
11689/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011690 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11691 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011692 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011693static union node *
11694parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011695{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011696 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011697
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011698 tokpushback = 0;
11699 doprompt = interact;
11700 if (doprompt)
11701 setprompt(doprompt);
11702 needprompt = 0;
11703 t = readtoken();
11704 if (t == TEOF)
11705 return NEOF;
11706 if (t == TNL)
11707 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011708 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011709 return list(1);
11710}
11711
11712/*
11713 * Input any here documents.
11714 */
11715static void
11716parseheredoc(void)
11717{
11718 struct heredoc *here;
11719 union node *n;
11720
11721 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011722 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011723
11724 while (here) {
11725 if (needprompt) {
11726 setprompt(2);
11727 }
11728 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11729 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011730 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011731 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011732 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011733 n->narg.text = wordtext;
11734 n->narg.backquote = backquotelist;
11735 here->here->nhere.doc = n;
11736 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011737 }
Eric Andersencb57d552001-06-28 07:25:16 +000011738}
11739
11740
11741/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011742 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011743 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011744#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011745static const char *
11746expandstr(const char *ps)
11747{
11748 union node n;
11749
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011750 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11751 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011752 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011753 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011754 popfile();
11755
11756 n.narg.type = NARG;
11757 n.narg.next = NULL;
11758 n.narg.text = wordtext;
11759 n.narg.backquote = backquotelist;
11760
11761 expandarg(&n, NULL, 0);
11762 return stackblock();
11763}
11764#endif
11765
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011766/*
11767 * Execute a command or commands contained in a string.
11768 */
11769static int
11770evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011771{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011772 union node *n;
11773 struct stackmark smark;
11774 int skip;
11775
11776 setinputstring(s);
11777 setstackmark(&smark);
11778
11779 skip = 0;
11780 while ((n = parsecmd(0)) != NEOF) {
11781 evaltree(n, 0);
11782 popstackmark(&smark);
11783 skip = evalskip;
11784 if (skip)
11785 break;
11786 }
11787 popfile();
11788
11789 skip &= mask;
11790 evalskip = skip;
11791 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011792}
11793
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011794/*
11795 * The eval command.
11796 */
11797static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011798evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011799{
11800 char *p;
11801 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011802
Denis Vlasenko68404f12008-03-17 09:00:54 +000011803 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011804 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011805 argv += 2;
11806 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011807 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011808 for (;;) {
11809 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011810 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011811 if (p == NULL)
11812 break;
11813 STPUTC(' ', concat);
11814 }
11815 STPUTC('\0', concat);
11816 p = grabstackstr(concat);
11817 }
11818 evalstring(p, ~SKIPEVAL);
11819
11820 }
11821 return exitstatus;
11822}
11823
11824/*
11825 * Read and execute commands. "Top" is nonzero for the top level command
11826 * loop; it turns on prompting if the shell is interactive.
11827 */
11828static int
11829cmdloop(int top)
11830{
11831 union node *n;
11832 struct stackmark smark;
11833 int inter;
11834 int numeof = 0;
11835
11836 TRACE(("cmdloop(%d) called\n", top));
11837 for (;;) {
11838 int skip;
11839
11840 setstackmark(&smark);
11841#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011842 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011843 showjobs(stderr, SHOW_CHANGED);
11844#endif
11845 inter = 0;
11846 if (iflag && top) {
11847 inter++;
11848#if ENABLE_ASH_MAIL
11849 chkmail();
11850#endif
11851 }
11852 n = parsecmd(inter);
11853 /* showtree(n); DEBUG */
11854 if (n == NEOF) {
11855 if (!top || numeof >= 50)
11856 break;
11857 if (!stoppedjobs()) {
11858 if (!Iflag)
11859 break;
11860 out2str("\nUse \"exit\" to leave shell.\n");
11861 }
11862 numeof++;
11863 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011864 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11865 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011866 numeof = 0;
11867 evaltree(n, 0);
11868 }
11869 popstackmark(&smark);
11870 skip = evalskip;
11871
11872 if (skip) {
11873 evalskip = 0;
11874 return skip & SKIPEVAL;
11875 }
11876 }
11877 return 0;
11878}
11879
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011880/*
11881 * Take commands from a file. To be compatible we should do a path
11882 * search for the file, which is necessary to find sub-commands.
11883 */
11884static char *
11885find_dot_file(char *name)
11886{
11887 char *fullname;
11888 const char *path = pathval();
11889 struct stat statb;
11890
11891 /* don't try this for absolute or relative paths */
11892 if (strchr(name, '/'))
11893 return name;
11894
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011895 /* IIRC standards do not say whether . is to be searched.
11896 * And it is even smaller this way, making it unconditional for now:
11897 */
11898 if (1) { /* ENABLE_ASH_BASH_COMPAT */
11899 fullname = name;
11900 goto try_cur_dir;
11901 }
11902
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011903 while ((fullname = padvance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011904 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011905 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11906 /*
11907 * Don't bother freeing here, since it will
11908 * be freed by the caller.
11909 */
11910 return fullname;
11911 }
11912 stunalloc(fullname);
11913 }
11914
11915 /* not found in the PATH */
11916 ash_msg_and_raise_error("%s: not found", name);
11917 /* NOTREACHED */
11918}
11919
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011920static int
11921dotcmd(int argc, char **argv)
11922{
11923 struct strlist *sp;
11924 volatile struct shparam saveparam;
11925 int status = 0;
11926
11927 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011928 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011929
Denis Vlasenko68404f12008-03-17 09:00:54 +000011930 if (argv[1]) { /* That's what SVR2 does */
11931 char *fullname = find_dot_file(argv[1]);
11932 argv += 2;
11933 argc -= 2;
11934 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011935 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011936 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011937 shellparam.nparam = argc;
11938 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011939 };
11940
11941 setinputfile(fullname, INPUT_PUSH_FILE);
11942 commandname = fullname;
11943 cmdloop(0);
11944 popfile();
11945
Denis Vlasenko68404f12008-03-17 09:00:54 +000011946 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011947 freeparam(&shellparam);
11948 shellparam = saveparam;
11949 };
11950 status = exitstatus;
11951 }
11952 return status;
11953}
11954
11955static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011956exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011957{
11958 if (stoppedjobs())
11959 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011960 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011961 exitstatus = number(argv[1]);
11962 raise_exception(EXEXIT);
11963 /* NOTREACHED */
11964}
11965
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011966/*
11967 * Read a file containing shell functions.
11968 */
11969static void
11970readcmdfile(char *name)
11971{
11972 setinputfile(name, INPUT_PUSH_FILE);
11973 cmdloop(0);
11974 popfile();
11975}
11976
11977
Denis Vlasenkocc571512007-02-23 21:10:35 +000011978/* ============ find_command inplementation */
11979
11980/*
11981 * Resolve a command name. If you change this routine, you may have to
11982 * change the shellexec routine as well.
11983 */
11984static void
11985find_command(char *name, struct cmdentry *entry, int act, const char *path)
11986{
11987 struct tblentry *cmdp;
11988 int idx;
11989 int prev;
11990 char *fullname;
11991 struct stat statb;
11992 int e;
11993 int updatetbl;
11994 struct builtincmd *bcmd;
11995
11996 /* If name contains a slash, don't use PATH or hash table */
11997 if (strchr(name, '/') != NULL) {
11998 entry->u.index = -1;
11999 if (act & DO_ABS) {
12000 while (stat(name, &statb) < 0) {
12001#ifdef SYSV
12002 if (errno == EINTR)
12003 continue;
12004#endif
12005 entry->cmdtype = CMDUNKNOWN;
12006 return;
12007 }
12008 }
12009 entry->cmdtype = CMDNORMAL;
12010 return;
12011 }
12012
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012013/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012014
12015 updatetbl = (path == pathval());
12016 if (!updatetbl) {
12017 act |= DO_ALTPATH;
12018 if (strstr(path, "%builtin") != NULL)
12019 act |= DO_ALTBLTIN;
12020 }
12021
12022 /* If name is in the table, check answer will be ok */
12023 cmdp = cmdlookup(name, 0);
12024 if (cmdp != NULL) {
12025 int bit;
12026
12027 switch (cmdp->cmdtype) {
12028 default:
12029#if DEBUG
12030 abort();
12031#endif
12032 case CMDNORMAL:
12033 bit = DO_ALTPATH;
12034 break;
12035 case CMDFUNCTION:
12036 bit = DO_NOFUNC;
12037 break;
12038 case CMDBUILTIN:
12039 bit = DO_ALTBLTIN;
12040 break;
12041 }
12042 if (act & bit) {
12043 updatetbl = 0;
12044 cmdp = NULL;
12045 } else if (cmdp->rehash == 0)
12046 /* if not invalidated by cd, we're done */
12047 goto success;
12048 }
12049
12050 /* If %builtin not in path, check for builtin next */
12051 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012052 if (bcmd) {
12053 if (IS_BUILTIN_REGULAR(bcmd))
12054 goto builtin_success;
12055 if (act & DO_ALTPATH) {
12056 if (!(act & DO_ALTBLTIN))
12057 goto builtin_success;
12058 } else if (builtinloc <= 0) {
12059 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012060 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012061 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012062
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012063#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012064 {
12065 int applet_no = find_applet_by_name(name);
12066 if (applet_no >= 0) {
12067 entry->cmdtype = CMDNORMAL;
12068 entry->u.index = -2 - applet_no;
12069 return;
12070 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012071 }
12072#endif
12073
Denis Vlasenkocc571512007-02-23 21:10:35 +000012074 /* We have to search path. */
12075 prev = -1; /* where to start */
12076 if (cmdp && cmdp->rehash) { /* doing a rehash */
12077 if (cmdp->cmdtype == CMDBUILTIN)
12078 prev = builtinloc;
12079 else
12080 prev = cmdp->param.index;
12081 }
12082
12083 e = ENOENT;
12084 idx = -1;
12085 loop:
12086 while ((fullname = padvance(&path, name)) != NULL) {
12087 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012088 /* NB: code below will still use fullname
12089 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012090 idx++;
12091 if (pathopt) {
12092 if (prefix(pathopt, "builtin")) {
12093 if (bcmd)
12094 goto builtin_success;
12095 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012096 }
12097 if ((act & DO_NOFUNC)
12098 || !prefix(pathopt, "func")
12099 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012100 continue;
12101 }
12102 }
12103 /* if rehash, don't redo absolute path names */
12104 if (fullname[0] == '/' && idx <= prev) {
12105 if (idx < prev)
12106 continue;
12107 TRACE(("searchexec \"%s\": no change\n", name));
12108 goto success;
12109 }
12110 while (stat(fullname, &statb) < 0) {
12111#ifdef SYSV
12112 if (errno == EINTR)
12113 continue;
12114#endif
12115 if (errno != ENOENT && errno != ENOTDIR)
12116 e = errno;
12117 goto loop;
12118 }
12119 e = EACCES; /* if we fail, this will be the error */
12120 if (!S_ISREG(statb.st_mode))
12121 continue;
12122 if (pathopt) { /* this is a %func directory */
12123 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012124 /* NB: stalloc will return space pointed by fullname
12125 * (because we don't have any intervening allocations
12126 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012127 readcmdfile(fullname);
12128 cmdp = cmdlookup(name, 0);
12129 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12130 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12131 stunalloc(fullname);
12132 goto success;
12133 }
12134 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12135 if (!updatetbl) {
12136 entry->cmdtype = CMDNORMAL;
12137 entry->u.index = idx;
12138 return;
12139 }
12140 INT_OFF;
12141 cmdp = cmdlookup(name, 1);
12142 cmdp->cmdtype = CMDNORMAL;
12143 cmdp->param.index = idx;
12144 INT_ON;
12145 goto success;
12146 }
12147
12148 /* We failed. If there was an entry for this command, delete it */
12149 if (cmdp && updatetbl)
12150 delete_cmd_entry();
12151 if (act & DO_ERR)
12152 ash_msg("%s: %s", name, errmsg(e, "not found"));
12153 entry->cmdtype = CMDUNKNOWN;
12154 return;
12155
12156 builtin_success:
12157 if (!updatetbl) {
12158 entry->cmdtype = CMDBUILTIN;
12159 entry->u.cmd = bcmd;
12160 return;
12161 }
12162 INT_OFF;
12163 cmdp = cmdlookup(name, 1);
12164 cmdp->cmdtype = CMDBUILTIN;
12165 cmdp->param.cmd = bcmd;
12166 INT_ON;
12167 success:
12168 cmdp->rehash = 0;
12169 entry->cmdtype = cmdp->cmdtype;
12170 entry->u = cmdp->param;
12171}
12172
12173
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012174/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012175
Eric Andersencb57d552001-06-28 07:25:16 +000012176/*
Eric Andersencb57d552001-06-28 07:25:16 +000012177 * The trap builtin.
12178 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012179static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012180trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012181{
12182 char *action;
12183 char **ap;
12184 int signo;
12185
Eric Andersenc470f442003-07-28 09:56:35 +000012186 nextopt(nullstr);
12187 ap = argptr;
12188 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012189 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012190 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012191 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012192 single_quote(trap[signo]),
12193 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012194 }
12195 }
12196 return 0;
12197 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012198 action = NULL;
12199 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012200 action = *ap++;
12201 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012202 signo = get_signum(*ap);
12203 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012204 ash_msg_and_raise_error("%s: bad trap", *ap);
12205 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012206 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012207 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012208 action = NULL;
12209 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012210 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012211 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012212 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012213 trap[signo] = action;
12214 if (signo != 0)
12215 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012216 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012217 ap++;
12218 }
12219 return 0;
12220}
12221
Eric Andersenc470f442003-07-28 09:56:35 +000012222
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012223/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012224
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012225#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012226/*
12227 * Lists available builtins
12228 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012229static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012230helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012231{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012232 unsigned col;
12233 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012234
12235 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012236 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012237 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012238 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012239 if (col > 60) {
12240 out1fmt("\n");
12241 col = 0;
12242 }
12243 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012244#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012245 {
12246 const char *a = applet_names;
12247 while (*a) {
12248 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12249 if (col > 60) {
12250 out1fmt("\n");
12251 col = 0;
12252 }
12253 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012254 }
12255 }
12256#endif
12257 out1fmt("\n\n");
12258 return EXIT_SUCCESS;
12259}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012260#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012261
Eric Andersencb57d552001-06-28 07:25:16 +000012262/*
Eric Andersencb57d552001-06-28 07:25:16 +000012263 * The export and readonly commands.
12264 */
Eric Andersenc470f442003-07-28 09:56:35 +000012265static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012266exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012267{
12268 struct var *vp;
12269 char *name;
12270 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012271 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012272 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012273
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012274 if (nextopt("p") != 'p') {
12275 aptr = argptr;
12276 name = *aptr;
12277 if (name) {
12278 do {
12279 p = strchr(name, '=');
12280 if (p != NULL) {
12281 p++;
12282 } else {
12283 vp = *findvar(hashvar(name), name);
12284 if (vp) {
12285 vp->flags |= flag;
12286 continue;
12287 }
Eric Andersencb57d552001-06-28 07:25:16 +000012288 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012289 setvar(name, p, flag);
12290 } while ((name = *++aptr) != NULL);
12291 return 0;
12292 }
Eric Andersencb57d552001-06-28 07:25:16 +000012293 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012294 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012295 return 0;
12296}
12297
Eric Andersencb57d552001-06-28 07:25:16 +000012298/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012299 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012300 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012301static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012302unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012303{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012304 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012305
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012306 cmdp = cmdlookup(name, 0);
12307 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12308 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012309}
12310
Eric Andersencb57d552001-06-28 07:25:16 +000012311/*
Eric Andersencb57d552001-06-28 07:25:16 +000012312 * The unset builtin command. We unset the function before we unset the
12313 * variable to allow a function to be unset when there is a readonly variable
12314 * with the same name.
12315 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012316static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012317unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012318{
12319 char **ap;
12320 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012321 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012322 int ret = 0;
12323
12324 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012325 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012326 }
Eric Andersencb57d552001-06-28 07:25:16 +000012327
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012328 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012329 if (flag != 'f') {
12330 i = unsetvar(*ap);
12331 ret |= i;
12332 if (!(i & 2))
12333 continue;
12334 }
12335 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012336 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012337 }
Eric Andersenc470f442003-07-28 09:56:35 +000012338 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012339}
12340
12341
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012342/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012343
Eric Andersenc470f442003-07-28 09:56:35 +000012344#include <sys/times.h>
12345
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012346static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012347 ' ', offsetof(struct tms, tms_utime),
12348 '\n', offsetof(struct tms, tms_stime),
12349 ' ', offsetof(struct tms, tms_cutime),
12350 '\n', offsetof(struct tms, tms_cstime),
12351 0
12352};
Eric Andersencb57d552001-06-28 07:25:16 +000012353
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012354static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012355timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012356{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012357 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012358 const unsigned char *p;
12359 struct tms buf;
12360
12361 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012362 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012363
12364 p = timescmd_str;
12365 do {
12366 t = *(clock_t *)(((char *) &buf) + p[1]);
12367 s = t / clk_tck;
12368 out1fmt("%ldm%ld.%.3lds%c",
12369 s/60, s%60,
12370 ((t - s * clk_tck) * 1000) / clk_tck,
12371 p[0]);
12372 } while (*(p += 2));
12373
Eric Andersencb57d552001-06-28 07:25:16 +000012374 return 0;
12375}
12376
Denis Vlasenko131ae172007-02-18 13:00:19 +000012377#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012378static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012379dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012380{
Eric Andersened9ecf72004-06-22 08:29:45 +000012381 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012382 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012383
Denis Vlasenkob012b102007-02-19 22:43:01 +000012384 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012385 result = arith(s, &errcode);
12386 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012387 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012388 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012389 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012390 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012391 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012392 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012393 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012394 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012395 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012396
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012397 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012398}
Eric Andersenc470f442003-07-28 09:56:35 +000012399
Eric Andersenc470f442003-07-28 09:56:35 +000012400/*
Eric Andersen90898442003-08-06 11:20:52 +000012401 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12402 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12403 *
12404 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012405 */
12406static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012407letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012408{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012409 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012410
Denis Vlasenko68404f12008-03-17 09:00:54 +000012411 argv++;
12412 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012413 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012414 do {
12415 i = dash_arith(*argv);
12416 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012417
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012418 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012419}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012420#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012421
Eric Andersenc470f442003-07-28 09:56:35 +000012422
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012423/* ============ miscbltin.c
12424 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012425 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012426 */
12427
12428#undef rflag
12429
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012430#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012431typedef enum __rlimit_resource rlim_t;
12432#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012433
Eric Andersenc470f442003-07-28 09:56:35 +000012434/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012435 * The read builtin. Options:
12436 * -r Do not interpret '\' specially
12437 * -s Turn off echo (tty only)
12438 * -n NCHARS Read NCHARS max
12439 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12440 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12441 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012442 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012443 * TODO: bash also has:
12444 * -a ARRAY Read into array[0],[1],etc
12445 * -d DELIM End on DELIM char, not newline
12446 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012447 */
Eric Andersenc470f442003-07-28 09:56:35 +000012448static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012449readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012450{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012451 static const char *const arg_REPLY[] = { "REPLY", NULL };
12452
Eric Andersenc470f442003-07-28 09:56:35 +000012453 char **ap;
12454 int backslash;
12455 char c;
12456 int rflag;
12457 char *prompt;
12458 const char *ifs;
12459 char *p;
12460 int startword;
12461 int status;
12462 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012463 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012464#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012465 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012466 int silent = 0;
12467 struct termios tty, old_tty;
12468#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012469#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012470 unsigned end_ms = 0;
12471 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012472#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012473
12474 rflag = 0;
12475 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012476 while ((i = nextopt("p:u:r"
12477 USE_ASH_READ_TIMEOUT("t:")
12478 USE_ASH_READ_NCHARS("n:s")
12479 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012480 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012481 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012482 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012483 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012484#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012485 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012486 nchars = bb_strtou(optionarg, NULL, 10);
12487 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012488 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012489 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012490 break;
12491 case 's':
12492 silent = 1;
12493 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012494#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012495#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012496 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012497 timeout = bb_strtou(optionarg, NULL, 10);
12498 if (errno || timeout > UINT_MAX / 2048)
12499 ash_msg_and_raise_error("invalid timeout");
12500 timeout *= 1000;
12501#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012502 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012503 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012504 /* EINVAL means number is ok, but not terminated by NUL */
12505 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012506 char *p2;
12507 if (*++p) {
12508 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012509 ts.tv_usec = bb_strtou(p, &p2, 10);
12510 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012511 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012512 scale = p2 - p;
12513 /* normalize to usec */
12514 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012515 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012516 while (scale++ < 6)
12517 ts.tv_usec *= 10;
12518 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012519 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012520 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012521 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012522 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012523 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012524 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012525#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012526 break;
12527#endif
12528 case 'r':
12529 rflag = 1;
12530 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012531 case 'u':
12532 fd = bb_strtou(optionarg, NULL, 10);
12533 if (fd < 0 || errno)
12534 ash_msg_and_raise_error("invalid file descriptor");
12535 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012536 default:
12537 break;
12538 }
Eric Andersenc470f442003-07-28 09:56:35 +000012539 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012540 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012541 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012542 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012543 ap = argptr;
12544 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012545 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012546 ifs = bltinlookup("IFS");
12547 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012548 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012549#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012550 tcgetattr(fd, &tty);
12551 old_tty = tty;
12552 if (nchars || silent) {
12553 if (nchars) {
12554 tty.c_lflag &= ~ICANON;
12555 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012556 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012557 if (silent) {
12558 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12559 }
12560 /* if tcgetattr failed, tcsetattr will fail too.
12561 * Ignoring, it's harmless. */
12562 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012563 }
12564#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012565
Eric Andersenc470f442003-07-28 09:56:35 +000012566 status = 0;
12567 startword = 1;
12568 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012569#if ENABLE_ASH_READ_TIMEOUT
12570 if (timeout) /* NB: ensuring end_ms is nonzero */
12571 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12572#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012573 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012574 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012575#if ENABLE_ASH_READ_TIMEOUT
12576 if (end_ms) {
12577 struct pollfd pfd[1];
12578 pfd[0].fd = fd;
12579 pfd[0].events = POLLIN;
12580 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12581 if ((int)timeout <= 0 /* already late? */
12582 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12583 ) { /* timed out! */
12584#if ENABLE_ASH_READ_NCHARS
12585 tcsetattr(fd, TCSANOW, &old_tty);
12586#endif
12587 return 1;
12588 }
12589 }
12590#endif
12591 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012592 status = 1;
12593 break;
12594 }
12595 if (c == '\0')
12596 continue;
12597 if (backslash) {
12598 backslash = 0;
12599 if (c != '\n')
12600 goto put;
12601 continue;
12602 }
12603 if (!rflag && c == '\\') {
12604 backslash++;
12605 continue;
12606 }
12607 if (c == '\n')
12608 break;
12609 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12610 continue;
12611 }
12612 startword = 0;
12613 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12614 STACKSTRNUL(p);
12615 setvar(*ap, stackblock(), 0);
12616 ap++;
12617 startword = 1;
12618 STARTSTACKSTR(p);
12619 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012620 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012621 STPUTC(c, p);
12622 }
12623 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012624/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012625#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012626 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012627#else
12628 while (1);
12629#endif
12630
12631#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012632 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012633#endif
12634
Eric Andersenc470f442003-07-28 09:56:35 +000012635 STACKSTRNUL(p);
12636 /* Remove trailing blanks */
12637 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12638 *p = '\0';
12639 setvar(*ap, stackblock(), 0);
12640 while (*++ap != NULL)
12641 setvar(*ap, nullstr, 0);
12642 return status;
12643}
12644
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012645static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012646umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012647{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012648 static const char permuser[3] ALIGN1 = "ugo";
12649 static const char permmode[3] ALIGN1 = "rwx";
12650 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012651 S_IRUSR, S_IWUSR, S_IXUSR,
12652 S_IRGRP, S_IWGRP, S_IXGRP,
12653 S_IROTH, S_IWOTH, S_IXOTH
12654 };
12655
12656 char *ap;
12657 mode_t mask;
12658 int i;
12659 int symbolic_mode = 0;
12660
12661 while (nextopt("S") != '\0') {
12662 symbolic_mode = 1;
12663 }
12664
Denis Vlasenkob012b102007-02-19 22:43:01 +000012665 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012666 mask = umask(0);
12667 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012668 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012669
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012670 ap = *argptr;
12671 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012672 if (symbolic_mode) {
12673 char buf[18];
12674 char *p = buf;
12675
12676 for (i = 0; i < 3; i++) {
12677 int j;
12678
12679 *p++ = permuser[i];
12680 *p++ = '=';
12681 for (j = 0; j < 3; j++) {
12682 if ((mask & permmask[3 * i + j]) == 0) {
12683 *p++ = permmode[j];
12684 }
12685 }
12686 *p++ = ',';
12687 }
12688 *--p = 0;
12689 puts(buf);
12690 } else {
12691 out1fmt("%.4o\n", mask);
12692 }
12693 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012694 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012695 mask = 0;
12696 do {
12697 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012698 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012699 mask = (mask << 3) + (*ap - '0');
12700 } while (*++ap != '\0');
12701 umask(mask);
12702 } else {
12703 mask = ~mask & 0777;
12704 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012705 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012706 }
12707 umask(~mask & 0777);
12708 }
12709 }
12710 return 0;
12711}
12712
12713/*
12714 * ulimit builtin
12715 *
12716 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12717 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12718 * ash by J.T. Conklin.
12719 *
12720 * Public domain.
12721 */
12722
12723struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012724 uint8_t cmd; /* RLIMIT_xxx fit into it */
12725 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012726 char option;
12727};
12728
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012729static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012730#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012731 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012732#endif
12733#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012734 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012735#endif
12736#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012737 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012738#endif
12739#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012740 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012741#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012742#ifdef RLIMIT_CORE
12743 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012744#endif
12745#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012746 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012747#endif
12748#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012749 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012750#endif
12751#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012752 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012753#endif
12754#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012755 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012756#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012757#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012758 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012759#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012760#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012761 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012762#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012763};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012764static const char limits_name[] =
12765#ifdef RLIMIT_CPU
12766 "time(seconds)" "\0"
12767#endif
12768#ifdef RLIMIT_FSIZE
12769 "file(blocks)" "\0"
12770#endif
12771#ifdef RLIMIT_DATA
12772 "data(kb)" "\0"
12773#endif
12774#ifdef RLIMIT_STACK
12775 "stack(kb)" "\0"
12776#endif
12777#ifdef RLIMIT_CORE
12778 "coredump(blocks)" "\0"
12779#endif
12780#ifdef RLIMIT_RSS
12781 "memory(kb)" "\0"
12782#endif
12783#ifdef RLIMIT_MEMLOCK
12784 "locked memory(kb)" "\0"
12785#endif
12786#ifdef RLIMIT_NPROC
12787 "process" "\0"
12788#endif
12789#ifdef RLIMIT_NOFILE
12790 "nofiles" "\0"
12791#endif
12792#ifdef RLIMIT_AS
12793 "vmemory(kb)" "\0"
12794#endif
12795#ifdef RLIMIT_LOCKS
12796 "locks" "\0"
12797#endif
12798;
Eric Andersenc470f442003-07-28 09:56:35 +000012799
Glenn L McGrath76620622004-01-13 10:19:37 +000012800enum limtype { SOFT = 0x1, HARD = 0x2 };
12801
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012802static void
12803printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012804 const struct limits *l)
12805{
12806 rlim_t val;
12807
12808 val = limit->rlim_max;
12809 if (how & SOFT)
12810 val = limit->rlim_cur;
12811
12812 if (val == RLIM_INFINITY)
12813 out1fmt("unlimited\n");
12814 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012815 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012816 out1fmt("%lld\n", (long long) val);
12817 }
12818}
12819
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012820static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012821ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012822{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012823 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012824 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012825 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012826 const struct limits *l;
12827 int set, all = 0;
12828 int optc, what;
12829 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012830
12831 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012832 while ((optc = nextopt("HSa"
12833#ifdef RLIMIT_CPU
12834 "t"
12835#endif
12836#ifdef RLIMIT_FSIZE
12837 "f"
12838#endif
12839#ifdef RLIMIT_DATA
12840 "d"
12841#endif
12842#ifdef RLIMIT_STACK
12843 "s"
12844#endif
12845#ifdef RLIMIT_CORE
12846 "c"
12847#endif
12848#ifdef RLIMIT_RSS
12849 "m"
12850#endif
12851#ifdef RLIMIT_MEMLOCK
12852 "l"
12853#endif
12854#ifdef RLIMIT_NPROC
12855 "p"
12856#endif
12857#ifdef RLIMIT_NOFILE
12858 "n"
12859#endif
12860#ifdef RLIMIT_AS
12861 "v"
12862#endif
12863#ifdef RLIMIT_LOCKS
12864 "w"
12865#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012866 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012867 switch (optc) {
12868 case 'H':
12869 how = HARD;
12870 break;
12871 case 'S':
12872 how = SOFT;
12873 break;
12874 case 'a':
12875 all = 1;
12876 break;
12877 default:
12878 what = optc;
12879 }
12880
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012881 for (l = limits_tbl; l->option != what; l++)
12882 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012883
12884 set = *argptr ? 1 : 0;
12885 if (set) {
12886 char *p = *argptr;
12887
12888 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012889 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012890 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012891 val = RLIM_INFINITY;
12892 else {
12893 val = (rlim_t) 0;
12894
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012895 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012896 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012897 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012898 if (val < (rlim_t) 0)
12899 break;
12900 }
12901 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012902 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012903 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012904 }
12905 }
12906 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012907 const char *lname = limits_name;
12908 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012909 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012910 out1fmt("%-20s ", lname);
12911 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012912 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012913 }
12914 return 0;
12915 }
12916
12917 getrlimit(l->cmd, &limit);
12918 if (set) {
12919 if (how & HARD)
12920 limit.rlim_max = val;
12921 if (how & SOFT)
12922 limit.rlim_cur = val;
12923 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012924 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012925 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012926 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012927 }
12928 return 0;
12929}
12930
Eric Andersen90898442003-08-06 11:20:52 +000012931
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012932/* ============ Math support */
12933
Denis Vlasenko131ae172007-02-18 13:00:19 +000012934#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012935
12936/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12937
12938 Permission is hereby granted, free of charge, to any person obtaining
12939 a copy of this software and associated documentation files (the
12940 "Software"), to deal in the Software without restriction, including
12941 without limitation the rights to use, copy, modify, merge, publish,
12942 distribute, sublicense, and/or sell copies of the Software, and to
12943 permit persons to whom the Software is furnished to do so, subject to
12944 the following conditions:
12945
12946 The above copyright notice and this permission notice shall be
12947 included in all copies or substantial portions of the Software.
12948
12949 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12950 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12951 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12952 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12953 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12954 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12955 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12956*/
12957
12958/* This is my infix parser/evaluator. It is optimized for size, intended
12959 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012960 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012961 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012962 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012963 * be that which POSIX specifies for shells. */
12964
12965/* The code uses a simple two-stack algorithm. See
12966 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012967 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012968 * this is based (this code differs in that it applies operators immediately
12969 * to the stack instead of adding them to a queue to end up with an
12970 * expression). */
12971
12972/* To use the routine, call it with an expression string and error return
12973 * pointer */
12974
12975/*
12976 * Aug 24, 2001 Manuel Novoa III
12977 *
12978 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12979 *
12980 * 1) In arith_apply():
12981 * a) Cached values of *numptr and &(numptr[-1]).
12982 * b) Removed redundant test for zero denominator.
12983 *
12984 * 2) In arith():
12985 * a) Eliminated redundant code for processing operator tokens by moving
12986 * to a table-based implementation. Also folded handling of parens
12987 * into the table.
12988 * b) Combined all 3 loops which called arith_apply to reduce generated
12989 * code size at the cost of speed.
12990 *
12991 * 3) The following expressions were treated as valid by the original code:
12992 * 1() , 0! , 1 ( *3 ) .
12993 * These bugs have been fixed by internally enclosing the expression in
12994 * parens and then checking that all binary ops and right parens are
12995 * preceded by a valid expression (NUM_TOKEN).
12996 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012997 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000012998 * ctype's isspace() if it is used by another busybox applet or if additional
12999 * whitespace chars should be considered. Look below the "#include"s for a
13000 * precompiler test.
13001 */
13002
13003/*
13004 * Aug 26, 2001 Manuel Novoa III
13005 *
13006 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
13007 *
13008 * Merge in Aaron's comments previously posted to the busybox list,
13009 * modified slightly to take account of my changes to the code.
13010 *
13011 */
13012
13013/*
13014 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
13015 *
13016 * - allow access to variable,
13017 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
13018 * - realize assign syntax (VAR=expr, +=, *= etc)
13019 * - realize exponentiation (** operator)
13020 * - realize comma separated - expr, expr
13021 * - realise ++expr --expr expr++ expr--
13022 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000013023 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000013024 * - was restored loses XOR operator
13025 * - remove one goto label, added three ;-)
13026 * - protect $((num num)) as true zero expr (Manuel`s error)
13027 * - always use special isspace(), see comment from bash ;-)
13028 */
13029
Eric Andersen90898442003-08-06 11:20:52 +000013030#define arith_isspace(arithval) \
13031 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13032
Eric Andersen90898442003-08-06 11:20:52 +000013033typedef unsigned char operator;
13034
13035/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000013036 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000013037 * precedence. The ID portion is so that multiple operators can have the
13038 * same precedence, ensuring that the leftmost one is evaluated first.
13039 * Consider * and /. */
13040
13041#define tok_decl(prec,id) (((id)<<5)|(prec))
13042#define PREC(op) ((op) & 0x1F)
13043
13044#define TOK_LPAREN tok_decl(0,0)
13045
13046#define TOK_COMMA tok_decl(1,0)
13047
13048#define TOK_ASSIGN tok_decl(2,0)
13049#define TOK_AND_ASSIGN tok_decl(2,1)
13050#define TOK_OR_ASSIGN tok_decl(2,2)
13051#define TOK_XOR_ASSIGN tok_decl(2,3)
13052#define TOK_PLUS_ASSIGN tok_decl(2,4)
13053#define TOK_MINUS_ASSIGN tok_decl(2,5)
13054#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13055#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13056
13057#define TOK_MUL_ASSIGN tok_decl(3,0)
13058#define TOK_DIV_ASSIGN tok_decl(3,1)
13059#define TOK_REM_ASSIGN tok_decl(3,2)
13060
13061/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013062#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000013063
13064/* conditional is right associativity too */
13065#define TOK_CONDITIONAL tok_decl(4,0)
13066#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13067
13068#define TOK_OR tok_decl(5,0)
13069
13070#define TOK_AND tok_decl(6,0)
13071
13072#define TOK_BOR tok_decl(7,0)
13073
13074#define TOK_BXOR tok_decl(8,0)
13075
13076#define TOK_BAND tok_decl(9,0)
13077
13078#define TOK_EQ tok_decl(10,0)
13079#define TOK_NE tok_decl(10,1)
13080
13081#define TOK_LT tok_decl(11,0)
13082#define TOK_GT tok_decl(11,1)
13083#define TOK_GE tok_decl(11,2)
13084#define TOK_LE tok_decl(11,3)
13085
13086#define TOK_LSHIFT tok_decl(12,0)
13087#define TOK_RSHIFT tok_decl(12,1)
13088
13089#define TOK_ADD tok_decl(13,0)
13090#define TOK_SUB tok_decl(13,1)
13091
13092#define TOK_MUL tok_decl(14,0)
13093#define TOK_DIV tok_decl(14,1)
13094#define TOK_REM tok_decl(14,2)
13095
13096/* exponent is right associativity */
13097#define TOK_EXPONENT tok_decl(15,1)
13098
13099/* For now unary operators. */
13100#define UNARYPREC 16
13101#define TOK_BNOT tok_decl(UNARYPREC,0)
13102#define TOK_NOT tok_decl(UNARYPREC,1)
13103
13104#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13105#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13106
13107#define PREC_PRE (UNARYPREC+2)
13108
13109#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13110#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13111
13112#define PREC_POST (UNARYPREC+3)
13113
13114#define TOK_POST_INC tok_decl(PREC_POST, 0)
13115#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13116
13117#define SPEC_PREC (UNARYPREC+4)
13118
13119#define TOK_NUM tok_decl(SPEC_PREC, 0)
13120#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13121
13122#define NUMPTR (*numstackptr)
13123
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013124static int
13125tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013126{
13127 operator prec = PREC(op);
13128
13129 convert_prec_is_assing(prec);
13130 return (prec == PREC(TOK_ASSIGN) ||
13131 prec == PREC_PRE || prec == PREC_POST);
13132}
13133
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013134static int
13135is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013136{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013137 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13138 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013139}
13140
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013141typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013142 arith_t val;
13143 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013144 char contidional_second_val_initialized;
13145 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013146 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013147} v_n_t;
13148
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013149typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013150 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013151 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013152} chk_var_recursive_looped_t;
13153
13154static chk_var_recursive_looped_t *prev_chk_var_recursive;
13155
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013156static int
13157arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013158{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013159 if (t->var) {
13160 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013161
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013162 if (p) {
13163 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013164
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013165 /* recursive try as expression */
13166 chk_var_recursive_looped_t *cur;
13167 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013168
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013169 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13170 if (strcmp(cur->var, t->var) == 0) {
13171 /* expression recursion loop detected */
13172 return -5;
13173 }
13174 }
13175 /* save current lookuped var name */
13176 cur = prev_chk_var_recursive;
13177 cur_save.var = t->var;
13178 cur_save.next = cur;
13179 prev_chk_var_recursive = &cur_save;
13180
13181 t->val = arith (p, &errcode);
13182 /* restore previous ptr after recursiving */
13183 prev_chk_var_recursive = cur;
13184 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013185 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013186 /* allow undefined var as 0 */
13187 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013188 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013189 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013190}
13191
13192/* "applying" a token means performing it on the top elements on the integer
13193 * stack. For a unary operator it will only change the top element, but a
13194 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013195static int
13196arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013197{
Eric Andersen90898442003-08-06 11:20:52 +000013198 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013199 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013200 int ret_arith_lookup_val;
13201
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013202 /* There is no operator that can work without arguments */
13203 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013204 numptr_m1 = NUMPTR - 1;
13205
13206 /* check operand is var with noninteger value */
13207 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013208 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013209 return ret_arith_lookup_val;
13210
13211 rez = numptr_m1->val;
13212 if (op == TOK_UMINUS)
13213 rez *= -1;
13214 else if (op == TOK_NOT)
13215 rez = !rez;
13216 else if (op == TOK_BNOT)
13217 rez = ~rez;
13218 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13219 rez++;
13220 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13221 rez--;
13222 else if (op != TOK_UPLUS) {
13223 /* Binary operators */
13224
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013225 /* check and binary operators need two arguments */
13226 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013227
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013228 /* ... and they pop one */
13229 --NUMPTR;
13230 numptr_val = rez;
13231 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013232 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013233 /* protect $((expr1 ? expr2)) without ": expr" */
13234 goto err;
13235 }
13236 rez = numptr_m1->contidional_second_val;
13237 } else if (numptr_m1->contidional_second_val_initialized) {
13238 /* protect $((expr1 : expr2)) without "expr ? " */
13239 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013240 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013241 numptr_m1 = NUMPTR - 1;
13242 if (op != TOK_ASSIGN) {
13243 /* check operand is var with noninteger value for not '=' */
13244 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13245 if (ret_arith_lookup_val)
13246 return ret_arith_lookup_val;
13247 }
13248 if (op == TOK_CONDITIONAL) {
13249 numptr_m1->contidional_second_val = rez;
13250 }
13251 rez = numptr_m1->val;
13252 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013253 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013254 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013255 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013256 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013257 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013258 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013259 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013260 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013261 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013262 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013263 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013264 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013265 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013267 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013268 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013269 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013270 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013271 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013272 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013273 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013274 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013275 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013276 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013277 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013278 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013279 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013280 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013281 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013282 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013283 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013284 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013285 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013286 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013287 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013288 /* protect $((expr : expr)) without "expr ? " */
13289 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013290 }
13291 numptr_m1->contidional_second_val_initialized = op;
13292 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013293 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013294 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013295 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013296 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013297 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013298 return -3; /* exponent less than 0 */
13299 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013300 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013301
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013302 if (numptr_val)
13303 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013304 c *= rez;
13305 rez = c;
13306 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013307 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013308 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013309 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013310 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013311 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013312 rez %= numptr_val;
13313 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013314 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013315 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013316
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013317 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013318 /* Hmm, 1=2 ? */
13319 goto err;
13320 }
13321 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013322#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013323 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013324#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013325 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013326#endif
Eric Andersen90898442003-08-06 11:20:52 +000013327 setvar(numptr_m1->var, buf, 0);
13328 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013329 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013330 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013331 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013332 rez++;
13333 }
13334 numptr_m1->val = rez;
13335 /* protect geting var value, is number now */
13336 numptr_m1->var = NULL;
13337 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013338 err:
13339 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013340}
13341
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013342/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013343static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013344 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13345 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13346 '<','<', 0, TOK_LSHIFT,
13347 '>','>', 0, TOK_RSHIFT,
13348 '|','|', 0, TOK_OR,
13349 '&','&', 0, TOK_AND,
13350 '!','=', 0, TOK_NE,
13351 '<','=', 0, TOK_LE,
13352 '>','=', 0, TOK_GE,
13353 '=','=', 0, TOK_EQ,
13354 '|','=', 0, TOK_OR_ASSIGN,
13355 '&','=', 0, TOK_AND_ASSIGN,
13356 '*','=', 0, TOK_MUL_ASSIGN,
13357 '/','=', 0, TOK_DIV_ASSIGN,
13358 '%','=', 0, TOK_REM_ASSIGN,
13359 '+','=', 0, TOK_PLUS_ASSIGN,
13360 '-','=', 0, TOK_MINUS_ASSIGN,
13361 '-','-', 0, TOK_POST_DEC,
13362 '^','=', 0, TOK_XOR_ASSIGN,
13363 '+','+', 0, TOK_POST_INC,
13364 '*','*', 0, TOK_EXPONENT,
13365 '!', 0, TOK_NOT,
13366 '<', 0, TOK_LT,
13367 '>', 0, TOK_GT,
13368 '=', 0, TOK_ASSIGN,
13369 '|', 0, TOK_BOR,
13370 '&', 0, TOK_BAND,
13371 '*', 0, TOK_MUL,
13372 '/', 0, TOK_DIV,
13373 '%', 0, TOK_REM,
13374 '+', 0, TOK_ADD,
13375 '-', 0, TOK_SUB,
13376 '^', 0, TOK_BXOR,
13377 /* uniq */
13378 '~', 0, TOK_BNOT,
13379 ',', 0, TOK_COMMA,
13380 '?', 0, TOK_CONDITIONAL,
13381 ':', 0, TOK_CONDITIONAL_SEP,
13382 ')', 0, TOK_RPAREN,
13383 '(', 0, TOK_LPAREN,
13384 0
13385};
13386/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013387#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013388
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013389static arith_t
13390arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013391{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013392 char arithval; /* Current character under analysis */
13393 operator lasttok, op;
13394 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013395 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013396 const char *p = endexpression;
13397 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013398 v_n_t *numstack, *numstackptr;
13399 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013400
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013401 /* Stack of integers */
13402 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13403 * in any given correct or incorrect expression is left as an exercise to
13404 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013405 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013406 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013407 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013408
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013409 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13410 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013411
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013412 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013413 arithval = *expr;
13414 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013415 if (p == endexpression) {
13416 /* Null expression. */
13417 return 0;
13418 }
13419
13420 /* This is only reached after all tokens have been extracted from the
13421 * input stream. If there are still tokens on the operator stack, they
13422 * are to be applied in order. At the end, there should be a final
13423 * result on the integer stack */
13424
13425 if (expr != endexpression + 1) {
13426 /* If we haven't done so already, */
13427 /* append a closing right paren */
13428 expr = endexpression;
13429 /* and let the loop process it. */
13430 continue;
13431 }
13432 /* At this point, we're done with the expression. */
13433 if (numstackptr != numstack+1) {
13434 /* ... but if there isn't, it's bad */
13435 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013436 *perrcode = -1;
13437 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013438 }
13439 if (numstack->var) {
13440 /* expression is $((var)) only, lookup now */
13441 errcode = arith_lookup_val(numstack);
13442 }
13443 ret:
13444 *perrcode = errcode;
13445 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013446 }
13447
Eric Andersen90898442003-08-06 11:20:52 +000013448 /* Continue processing the expression. */
13449 if (arith_isspace(arithval)) {
13450 /* Skip whitespace */
13451 goto prologue;
13452 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013453 p = endofname(expr);
13454 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013455 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013456
13457 numstackptr->var = alloca(var_name_size);
13458 safe_strncpy(numstackptr->var, expr, var_name_size);
13459 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013460 num:
Eric Andersen90898442003-08-06 11:20:52 +000013461 numstackptr->contidional_second_val_initialized = 0;
13462 numstackptr++;
13463 lasttok = TOK_NUM;
13464 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013465 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013466 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013467 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013468#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013469 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013470#else
13471 numstackptr->val = strtol(expr, (char **) &expr, 0);
13472#endif
Eric Andersen90898442003-08-06 11:20:52 +000013473 goto num;
13474 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013475 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013476 const char *o;
13477
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013478 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013479 /* strange operator not found */
13480 goto err;
13481 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013482 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013483 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013484 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013485 /* found */
13486 expr = o - 1;
13487 break;
13488 }
13489 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013490 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013491 p++;
13492 /* skip zero delim */
13493 p++;
13494 }
13495 op = p[1];
13496
13497 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013498 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13499 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013500
13501 /* Plus and minus are binary (not unary) _only_ if the last
13502 * token was as number, or a right paren (which pretends to be
13503 * a number, since it evaluates to one). Think about it.
13504 * It makes sense. */
13505 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013506 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013507 case TOK_ADD:
13508 op = TOK_UPLUS;
13509 break;
13510 case TOK_SUB:
13511 op = TOK_UMINUS;
13512 break;
13513 case TOK_POST_INC:
13514 op = TOK_PRE_INC;
13515 break;
13516 case TOK_POST_DEC:
13517 op = TOK_PRE_DEC;
13518 break;
Eric Andersen90898442003-08-06 11:20:52 +000013519 }
13520 }
13521 /* We don't want a unary operator to cause recursive descent on the
13522 * stack, because there can be many in a row and it could cause an
13523 * operator to be evaluated before its argument is pushed onto the
13524 * integer stack. */
13525 /* But for binary operators, "apply" everything on the operator
13526 * stack until we find an operator with a lesser priority than the
13527 * one we have just extracted. */
13528 /* Left paren is given the lowest priority so it will never be
13529 * "applied" in this way.
13530 * if associativity is right and priority eq, applied also skip
13531 */
13532 prec = PREC(op);
13533 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13534 /* not left paren or unary */
13535 if (lasttok != TOK_NUM) {
13536 /* binary op must be preceded by a num */
13537 goto err;
13538 }
13539 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013540 if (op == TOK_RPAREN) {
13541 /* The algorithm employed here is simple: while we don't
13542 * hit an open paren nor the bottom of the stack, pop
13543 * tokens and apply them */
13544 if (stackptr[-1] == TOK_LPAREN) {
13545 --stackptr;
13546 /* Any operator directly after a */
13547 lasttok = TOK_NUM;
13548 /* close paren should consider itself binary */
13549 goto prologue;
13550 }
13551 } else {
13552 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013553
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013554 convert_prec_is_assing(prec);
13555 convert_prec_is_assing(prev_prec);
13556 if (prev_prec < prec)
13557 break;
13558 /* check right assoc */
13559 if (prev_prec == prec && is_right_associativity(prec))
13560 break;
13561 }
13562 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13563 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013564 }
13565 if (op == TOK_RPAREN) {
13566 goto err;
13567 }
13568 }
13569
13570 /* Push this operator to the stack and remember it. */
13571 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013572 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013573 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013574 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013575}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013576#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013577
13578
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013579/* ============ main() and helpers */
13580
13581/*
13582 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013583 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013584static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013585static void
13586exitshell(void)
13587{
13588 struct jmploc loc;
13589 char *p;
13590 int status;
13591
13592 status = exitstatus;
13593 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13594 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013595 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013596/* dash bug: it just does _exit(exitstatus) here
13597 * but we have to do setjobctl(0) first!
13598 * (bug is still not fixed in dash-0.5.3 - if you run dash
13599 * under Midnight Commander, on exit from dash MC is backgrounded) */
13600 status = exitstatus;
13601 goto out;
13602 }
13603 exception_handler = &loc;
13604 p = trap[0];
13605 if (p) {
13606 trap[0] = NULL;
13607 evalstring(p, 0);
13608 }
13609 flush_stdout_stderr();
13610 out:
13611 setjobctl(0);
13612 _exit(status);
13613 /* NOTREACHED */
13614}
13615
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013616static void
13617init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013618{
13619 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013620 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013621
13622 /* from trap.c: */
13623 signal(SIGCHLD, SIG_DFL);
13624
13625 /* from var.c: */
13626 {
13627 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013628 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013629 const char *p;
13630 struct stat st1, st2;
13631
13632 initvar();
13633 for (envp = environ; envp && *envp; envp++) {
13634 if (strchr(*envp, '=')) {
13635 setvareq(*envp, VEXPORT|VTEXTFIXED);
13636 }
13637 }
13638
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013639 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013640 setvar("PPID", ppid, 0);
13641
13642 p = lookupvar("PWD");
13643 if (p)
13644 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13645 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13646 p = '\0';
13647 setpwd(p, 0);
13648 }
13649}
13650
13651/*
13652 * Process the shell command line arguments.
13653 */
13654static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013655procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013656{
13657 int i;
13658 const char *xminusc;
13659 char **xargv;
13660
13661 xargv = argv;
13662 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013663 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013664 xargv++;
13665 for (i = 0; i < NOPTS; i++)
13666 optlist[i] = 2;
13667 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013668 if (options(1)) {
13669 /* it already printed err message */
13670 raise_exception(EXERROR);
13671 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013672 xargv = argptr;
13673 xminusc = minusc;
13674 if (*xargv == NULL) {
13675 if (xminusc)
13676 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13677 sflag = 1;
13678 }
13679 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13680 iflag = 1;
13681 if (mflag == 2)
13682 mflag = iflag;
13683 for (i = 0; i < NOPTS; i++)
13684 if (optlist[i] == 2)
13685 optlist[i] = 0;
13686#if DEBUG == 2
13687 debug = 1;
13688#endif
13689 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13690 if (xminusc) {
13691 minusc = *xargv++;
13692 if (*xargv)
13693 goto setarg0;
13694 } else if (!sflag) {
13695 setinputfile(*xargv, 0);
13696 setarg0:
13697 arg0 = *xargv++;
13698 commandname = arg0;
13699 }
13700
13701 shellparam.p = xargv;
13702#if ENABLE_ASH_GETOPTS
13703 shellparam.optind = 1;
13704 shellparam.optoff = -1;
13705#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013706 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013707 while (*xargv) {
13708 shellparam.nparam++;
13709 xargv++;
13710 }
13711 optschanged();
13712}
13713
13714/*
13715 * Read /etc/profile or .profile.
13716 */
13717static void
13718read_profile(const char *name)
13719{
13720 int skip;
13721
13722 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13723 return;
13724 skip = cmdloop(0);
13725 popfile();
13726 if (skip)
13727 exitshell();
13728}
13729
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013730/*
13731 * This routine is called when an error or an interrupt occurs in an
13732 * interactive shell and control is returned to the main command loop.
13733 */
13734static void
13735reset(void)
13736{
13737 /* from eval.c: */
13738 evalskip = 0;
13739 loopnest = 0;
13740 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013741 g_parsefile->left_in_buffer = 0;
13742 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013743 popallfiles();
13744 /* from parser.c: */
13745 tokpushback = 0;
13746 checkkwd = 0;
13747 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013748 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013749}
13750
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013751#if PROFILE
13752static short profile_buf[16384];
13753extern int etext();
13754#endif
13755
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013756/*
13757 * Main routine. We initialize things, parse the arguments, execute
13758 * profiles if we're a login shell, and then call cmdloop to execute
13759 * commands. The setjmp call sets up the location to jump to when an
13760 * exception occurs. When an exception occurs the variable "state"
13761 * is used to figure out how far we had gotten.
13762 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013763int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013764int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013765{
13766 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013767 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013768 struct jmploc jmploc;
13769 struct stackmark smark;
13770
Denis Vlasenko01631112007-12-16 17:20:38 +000013771 /* Initialize global data */
13772 INIT_G_misc();
13773 INIT_G_memstack();
13774 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013775#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013776 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013777#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013778 INIT_G_cmdtable();
13779
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013780#if PROFILE
13781 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13782#endif
13783
13784#if ENABLE_FEATURE_EDITING
13785 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13786#endif
13787 state = 0;
13788 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013789 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013790 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013791
13792 reset();
13793
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013794 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013795 if (e == EXERROR)
13796 exitstatus = 2;
13797 s = state;
13798 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13799 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013800 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013801 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013802
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013803 popstackmark(&smark);
13804 FORCE_INT_ON; /* enable interrupts */
13805 if (s == 1)
13806 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013807 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013808 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013809 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013810 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013811 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013812 }
13813 exception_handler = &jmploc;
13814#if DEBUG
13815 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013816 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013817 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013818#endif
13819 rootpid = getpid();
13820
13821#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013822 /* Can use monotonic_ns() for better randomness but for now it is
13823 * not used anywhere else in busybox... so avoid bloat */
13824 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013825#endif
13826 init();
13827 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013828 procargs(argv);
13829
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013830#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13831 if (iflag) {
13832 const char *hp = lookupvar("HISTFILE");
13833
13834 if (hp == NULL) {
13835 hp = lookupvar("HOME");
13836 if (hp != NULL) {
13837 char *defhp = concat_path_file(hp, ".ash_history");
13838 setvar("HISTFILE", defhp, 0);
13839 free(defhp);
13840 }
13841 }
13842 }
13843#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013844 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013845 isloginsh = 1;
13846 if (isloginsh) {
13847 state = 1;
13848 read_profile("/etc/profile");
13849 state1:
13850 state = 2;
13851 read_profile(".profile");
13852 }
13853 state2:
13854 state = 3;
13855 if (
13856#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013857 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013858#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013859 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013860 ) {
13861 shinit = lookupvar("ENV");
13862 if (shinit != NULL && *shinit != '\0') {
13863 read_profile(shinit);
13864 }
13865 }
13866 state3:
13867 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013868 if (minusc) {
13869 /* evalstring pushes parsefile stack.
13870 * Ensure we don't falsely claim that 0 (stdin)
13871 * is one of stacked source fds */
13872 if (!sflag)
13873 g_parsefile->fd = -1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013874 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013875 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013876
13877 if (sflag || minusc == NULL) {
13878#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013879 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013880 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013881 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013882 line_input_state->hist_file = hp;
13883 }
13884#endif
13885 state4: /* XXX ??? - why isn't this before the "if" statement */
13886 cmdloop(1);
13887 }
13888#if PROFILE
13889 monitor(0);
13890#endif
13891#ifdef GPROF
13892 {
13893 extern void _mcleanup(void);
13894 _mcleanup();
13895 }
13896#endif
13897 exitshell();
13898 /* NOTREACHED */
13899}
13900
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013901
Eric Andersendf82f612001-06-28 07:46:40 +000013902/*-
13903 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013904 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013905 *
13906 * This code is derived from software contributed to Berkeley by
13907 * Kenneth Almquist.
13908 *
13909 * Redistribution and use in source and binary forms, with or without
13910 * modification, are permitted provided that the following conditions
13911 * are met:
13912 * 1. Redistributions of source code must retain the above copyright
13913 * notice, this list of conditions and the following disclaimer.
13914 * 2. Redistributions in binary form must reproduce the above copyright
13915 * notice, this list of conditions and the following disclaimer in the
13916 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013917 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013918 * may be used to endorse or promote products derived from this software
13919 * without specific prior written permission.
13920 *
13921 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13922 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13923 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13924 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13925 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13926 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13927 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13928 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13929 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13930 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13931 * SUCH DAMAGE.
13932 */