blob: 13b4329b55f25fc433d6473754f21e65f236370f [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
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000263# define close(fd) do { \
264 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000265 if (close(dfd) < 0) \
266 bb_error_msg("bug on %d: closing %d(%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000267 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000268} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000269#else
270# define TRACE(param)
271# define TRACEV(param)
272#endif
273
274
Denis Vlasenko559691a2008-10-05 18:39:31 +0000275/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000276#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
277
278/* C99 say: "char" declaration may be signed or unsigned by default */
279#define signed_char2int(sc) ((int)(signed char)(sc))
280
Denis Vlasenko559691a2008-10-05 18:39:31 +0000281static int isdigit_str9(const char *str)
282{
283 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
284 while (--maxlen && isdigit(*str))
285 str++;
286 return (*str == '\0');
287}
Denis Vlasenko01631112007-12-16 17:20:38 +0000288
Denis Vlasenko559691a2008-10-05 18:39:31 +0000289
290/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000291/*
Eric Andersen2870d962001-07-02 17:27:21 +0000292 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000293 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000294 * much more efficient and portable. (But hacking the kernel is so much
295 * more fun than worrying about efficiency and portability. :-))
296 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000297#define INT_OFF do { \
298 suppressint++; \
299 xbarrier(); \
300} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000301
302/*
303 * Called to raise an exception. Since C doesn't include exceptions, we
304 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000305 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000306 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000307static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000308static void
309raise_exception(int e)
310{
311#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000312 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000313 abort();
314#endif
315 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000316 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000317 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000318}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000319#if DEBUG
320#define raise_exception(e) do { \
321 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
322 raise_exception(e); \
323} while (0)
324#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000325
326/*
327 * Called from trap.c when a SIGINT is received. (If the user specifies
328 * that SIGINT is to be trapped or ignored using the trap builtin, then
329 * this routine is not called.) Suppressint is nonzero when interrupts
330 * are held using the INT_OFF macro. (The test for iflag is just
331 * defensive programming.)
332 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000333static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000334static void
335raise_interrupt(void)
336{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000337 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000338
339 intpending = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000340 /* Signal is not automatically unmasked after it is raised,
341 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000342 sigprocmask_allsigs(SIG_UNBLOCK);
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000343 /* pendingsig = 0; - now done in onsig() */
344
Denis Vlasenko4b875702009-03-19 13:30:04 +0000345 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000346 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
347 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000348 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349 signal(SIGINT, SIG_DFL);
350 raise(SIGINT);
351 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000352 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000353 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000354 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000355 /* NOTREACHED */
356}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000357#if DEBUG
358#define raise_interrupt() do { \
359 TRACE(("raising interrupt on line %d\n", __LINE__)); \
360 raise_interrupt(); \
361} while (0)
362#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000363
364#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000365static void
366int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000367{
368 if (--suppressint == 0 && intpending) {
369 raise_interrupt();
370 }
371}
372#define INT_ON int_on()
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000373static void
374force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375{
376 suppressint = 0;
377 if (intpending)
378 raise_interrupt();
379}
380#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000381
382#else /* !ASH_OPTIMIZE_FOR_SIZE */
383
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000384#define INT_ON do { \
385 xbarrier(); \
386 if (--suppressint == 0 && intpending) \
387 raise_interrupt(); \
388} while (0)
389#define FORCE_INT_ON do { \
390 xbarrier(); \
391 suppressint = 0; \
392 if (intpending) \
393 raise_interrupt(); \
394} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000395#endif /* !ASH_OPTIMIZE_FOR_SIZE */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000396
397#define SAVE_INT(v) ((v) = suppressint)
398
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000399#define RESTORE_INT(v) do { \
400 xbarrier(); \
401 suppressint = (v); \
402 if (suppressint == 0 && intpending) \
403 raise_interrupt(); \
404} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000405
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000406
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000407/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000408
Eric Andersenc470f442003-07-28 09:56:35 +0000409static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000410outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000411{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000412 INT_OFF;
413 fputs(p, file);
414 INT_ON;
415}
416
417static void
418flush_stdout_stderr(void)
419{
420 INT_OFF;
421 fflush(stdout);
422 fflush(stderr);
423 INT_ON;
424}
425
426static void
427flush_stderr(void)
428{
429 INT_OFF;
430 fflush(stderr);
431 INT_ON;
432}
433
434static void
435outcslow(int c, FILE *dest)
436{
437 INT_OFF;
438 putc(c, dest);
439 fflush(dest);
440 INT_ON;
441}
442
443static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
444static int
445out1fmt(const char *fmt, ...)
446{
447 va_list ap;
448 int r;
449
450 INT_OFF;
451 va_start(ap, fmt);
452 r = vprintf(fmt, ap);
453 va_end(ap);
454 INT_ON;
455 return r;
456}
457
458static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
459static int
460fmtstr(char *outbuf, size_t length, const char *fmt, ...)
461{
462 va_list ap;
463 int ret;
464
465 va_start(ap, fmt);
466 INT_OFF;
467 ret = vsnprintf(outbuf, length, fmt, ap);
468 va_end(ap);
469 INT_ON;
470 return ret;
471}
472
473static void
474out1str(const char *p)
475{
476 outstr(p, stdout);
477}
478
479static void
480out2str(const char *p)
481{
482 outstr(p, stderr);
483 flush_stderr();
484}
485
486
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000487/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000488
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000489/* control characters in argument strings */
490#define CTLESC '\201' /* escape next character */
491#define CTLVAR '\202' /* variable defn */
492#define CTLENDVAR '\203'
493#define CTLBACKQ '\204'
494#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
495/* CTLBACKQ | CTLQUOTE == '\205' */
496#define CTLARI '\206' /* arithmetic expression */
497#define CTLENDARI '\207'
498#define CTLQUOTEMARK '\210'
499
500/* variable substitution byte (follows CTLVAR) */
501#define VSTYPE 0x0f /* type of variable substitution */
502#define VSNUL 0x10 /* colon--treat the empty string as unset */
503#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
504
505/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000506#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
507#define VSMINUS 0x2 /* ${var-text} */
508#define VSPLUS 0x3 /* ${var+text} */
509#define VSQUESTION 0x4 /* ${var?message} */
510#define VSASSIGN 0x5 /* ${var=text} */
511#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
512#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
513#define VSTRIMLEFT 0x8 /* ${var#pattern} */
514#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
515#define VSLENGTH 0xa /* ${#var} */
516#if ENABLE_ASH_BASH_COMPAT
517#define VSSUBSTR 0xc /* ${var:position:length} */
518#define VSREPLACE 0xd /* ${var/pattern/replacement} */
519#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
520#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000521
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000522static const char dolatstr[] ALIGN1 = {
523 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
524};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000525
Denis Vlasenko559691a2008-10-05 18:39:31 +0000526#define NCMD 0
527#define NPIPE 1
528#define NREDIR 2
529#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000530#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000531#define NAND 5
532#define NOR 6
533#define NSEMI 7
534#define NIF 8
535#define NWHILE 9
536#define NUNTIL 10
537#define NFOR 11
538#define NCASE 12
539#define NCLIST 13
540#define NDEFUN 14
541#define NARG 15
542#define NTO 16
543#if ENABLE_ASH_BASH_COMPAT
544#define NTO2 17
545#endif
546#define NCLOBBER 18
547#define NFROM 19
548#define NFROMTO 20
549#define NAPPEND 21
550#define NTOFD 22
551#define NFROMFD 23
552#define NHERE 24
553#define NXHERE 25
554#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000555#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000556
557union node;
558
559struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000560 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000561 union node *assign;
562 union node *args;
563 union node *redirect;
564};
565
566struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000567 smallint type;
568 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000569 struct nodelist *cmdlist;
570};
571
572struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000573 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000574 union node *n;
575 union node *redirect;
576};
577
578struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000579 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000580 union node *ch1;
581 union node *ch2;
582};
583
584struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000585 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000586 union node *test;
587 union node *ifpart;
588 union node *elsepart;
589};
590
591struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000592 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000593 union node *args;
594 union node *body;
595 char *var;
596};
597
598struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000599 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000600 union node *expr;
601 union node *cases;
602};
603
604struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000605 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000606 union node *next;
607 union node *pattern;
608 union node *body;
609};
610
611struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000612 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000613 union node *next;
614 char *text;
615 struct nodelist *backquote;
616};
617
Denis Vlasenko559691a2008-10-05 18:39:31 +0000618/* nfile and ndup layout must match!
619 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
620 * that it is actually NTO2 (>&file), and change its type.
621 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000622struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000623 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000624 union node *next;
625 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000626 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000627 union node *fname;
628 char *expfname;
629};
630
631struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000632 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000633 union node *next;
634 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000635 int dupfd;
636 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000637 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000638};
639
640struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000641 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000642 union node *next;
643 int fd;
644 union node *doc;
645};
646
647struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000648 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000649 union node *com;
650};
651
652union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000653 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000654 struct ncmd ncmd;
655 struct npipe npipe;
656 struct nredir nredir;
657 struct nbinary nbinary;
658 struct nif nif;
659 struct nfor nfor;
660 struct ncase ncase;
661 struct nclist nclist;
662 struct narg narg;
663 struct nfile nfile;
664 struct ndup ndup;
665 struct nhere nhere;
666 struct nnot nnot;
667};
668
669struct nodelist {
670 struct nodelist *next;
671 union node *n;
672};
673
674struct funcnode {
675 int count;
676 union node n;
677};
678
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000679/*
680 * Free a parse tree.
681 */
682static void
683freefunc(struct funcnode *f)
684{
685 if (f && --f->count < 0)
686 free(f);
687}
688
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000689
690/* ============ Debugging output */
691
692#if DEBUG
693
694static FILE *tracefile;
695
696static void
697trace_printf(const char *fmt, ...)
698{
699 va_list va;
700
701 if (debug != 1)
702 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000703 if (DEBUG_TIME)
704 fprintf(tracefile, "%u ", (int) time(NULL));
705 if (DEBUG_PID)
706 fprintf(tracefile, "[%u] ", (int) getpid());
707 if (DEBUG_SIG)
708 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000709 va_start(va, fmt);
710 vfprintf(tracefile, fmt, va);
711 va_end(va);
712}
713
714static void
715trace_vprintf(const char *fmt, va_list va)
716{
717 if (debug != 1)
718 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000719 if (DEBUG_TIME)
720 fprintf(tracefile, "%u ", (int) time(NULL));
721 if (DEBUG_PID)
722 fprintf(tracefile, "[%u] ", (int) getpid());
723 if (DEBUG_SIG)
724 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000725 vfprintf(tracefile, fmt, va);
726}
727
728static void
729trace_puts(const char *s)
730{
731 if (debug != 1)
732 return;
733 fputs(s, tracefile);
734}
735
736static void
737trace_puts_quoted(char *s)
738{
739 char *p;
740 char c;
741
742 if (debug != 1)
743 return;
744 putc('"', tracefile);
745 for (p = s; *p; p++) {
746 switch (*p) {
747 case '\n': c = 'n'; goto backslash;
748 case '\t': c = 't'; goto backslash;
749 case '\r': c = 'r'; goto backslash;
750 case '"': c = '"'; goto backslash;
751 case '\\': c = '\\'; goto backslash;
752 case CTLESC: c = 'e'; goto backslash;
753 case CTLVAR: c = 'v'; goto backslash;
754 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
755 case CTLBACKQ: c = 'q'; goto backslash;
756 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
757 backslash:
758 putc('\\', tracefile);
759 putc(c, tracefile);
760 break;
761 default:
762 if (*p >= ' ' && *p <= '~')
763 putc(*p, tracefile);
764 else {
765 putc('\\', tracefile);
766 putc(*p >> 6 & 03, tracefile);
767 putc(*p >> 3 & 07, tracefile);
768 putc(*p & 07, tracefile);
769 }
770 break;
771 }
772 }
773 putc('"', tracefile);
774}
775
776static void
777trace_puts_args(char **ap)
778{
779 if (debug != 1)
780 return;
781 if (!*ap)
782 return;
783 while (1) {
784 trace_puts_quoted(*ap);
785 if (!*++ap) {
786 putc('\n', tracefile);
787 break;
788 }
789 putc(' ', tracefile);
790 }
791}
792
793static void
794opentrace(void)
795{
796 char s[100];
797#ifdef O_APPEND
798 int flags;
799#endif
800
801 if (debug != 1) {
802 if (tracefile)
803 fflush(tracefile);
804 /* leave open because libedit might be using it */
805 return;
806 }
807 strcpy(s, "./trace");
808 if (tracefile) {
809 if (!freopen(s, "a", tracefile)) {
810 fprintf(stderr, "Can't re-open %s\n", s);
811 debug = 0;
812 return;
813 }
814 } else {
815 tracefile = fopen(s, "a");
816 if (tracefile == NULL) {
817 fprintf(stderr, "Can't open %s\n", s);
818 debug = 0;
819 return;
820 }
821 }
822#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000823 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000824 if (flags >= 0)
825 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
826#endif
827 setlinebuf(tracefile);
828 fputs("\nTracing started.\n", tracefile);
829}
830
831static void
832indent(int amount, char *pfx, FILE *fp)
833{
834 int i;
835
836 for (i = 0; i < amount; i++) {
837 if (pfx && i == amount - 1)
838 fputs(pfx, fp);
839 putc('\t', fp);
840 }
841}
842
843/* little circular references here... */
844static void shtree(union node *n, int ind, char *pfx, FILE *fp);
845
846static void
847sharg(union node *arg, FILE *fp)
848{
849 char *p;
850 struct nodelist *bqlist;
851 int subtype;
852
853 if (arg->type != NARG) {
854 out1fmt("<node type %d>\n", arg->type);
855 abort();
856 }
857 bqlist = arg->narg.backquote;
858 for (p = arg->narg.text; *p; p++) {
859 switch (*p) {
860 case CTLESC:
861 putc(*++p, fp);
862 break;
863 case CTLVAR:
864 putc('$', fp);
865 putc('{', fp);
866 subtype = *++p;
867 if (subtype == VSLENGTH)
868 putc('#', fp);
869
870 while (*p != '=')
871 putc(*p++, fp);
872
873 if (subtype & VSNUL)
874 putc(':', fp);
875
876 switch (subtype & VSTYPE) {
877 case VSNORMAL:
878 putc('}', fp);
879 break;
880 case VSMINUS:
881 putc('-', fp);
882 break;
883 case VSPLUS:
884 putc('+', fp);
885 break;
886 case VSQUESTION:
887 putc('?', fp);
888 break;
889 case VSASSIGN:
890 putc('=', fp);
891 break;
892 case VSTRIMLEFT:
893 putc('#', fp);
894 break;
895 case VSTRIMLEFTMAX:
896 putc('#', fp);
897 putc('#', fp);
898 break;
899 case VSTRIMRIGHT:
900 putc('%', fp);
901 break;
902 case VSTRIMRIGHTMAX:
903 putc('%', fp);
904 putc('%', fp);
905 break;
906 case VSLENGTH:
907 break;
908 default:
909 out1fmt("<subtype %d>", subtype);
910 }
911 break;
912 case CTLENDVAR:
913 putc('}', fp);
914 break;
915 case CTLBACKQ:
916 case CTLBACKQ|CTLQUOTE:
917 putc('$', fp);
918 putc('(', fp);
919 shtree(bqlist->n, -1, NULL, fp);
920 putc(')', fp);
921 break;
922 default:
923 putc(*p, fp);
924 break;
925 }
926 }
927}
928
929static void
930shcmd(union node *cmd, FILE *fp)
931{
932 union node *np;
933 int first;
934 const char *s;
935 int dftfd;
936
937 first = 1;
938 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000939 if (!first)
940 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000941 sharg(np, fp);
942 first = 0;
943 }
944 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000945 if (!first)
946 putc(' ', fp);
947 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000948 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000949 case NTO: s = ">>"+1; dftfd = 1; break;
950 case NCLOBBER: s = ">|"; dftfd = 1; break;
951 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000952#if ENABLE_ASH_BASH_COMPAT
953 case NTO2:
954#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000955 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000956 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +0000957 case NFROMFD: s = "<&"; break;
958 case NFROMTO: s = "<>"; break;
959 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000960 }
961 if (np->nfile.fd != dftfd)
962 fprintf(fp, "%d", np->nfile.fd);
963 fputs(s, fp);
964 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
965 fprintf(fp, "%d", np->ndup.dupfd);
966 } else {
967 sharg(np->nfile.fname, fp);
968 }
969 first = 0;
970 }
971}
972
973static void
974shtree(union node *n, int ind, char *pfx, FILE *fp)
975{
976 struct nodelist *lp;
977 const char *s;
978
979 if (n == NULL)
980 return;
981
982 indent(ind, pfx, fp);
983 switch (n->type) {
984 case NSEMI:
985 s = "; ";
986 goto binop;
987 case NAND:
988 s = " && ";
989 goto binop;
990 case NOR:
991 s = " || ";
992 binop:
993 shtree(n->nbinary.ch1, ind, NULL, fp);
994 /* if (ind < 0) */
995 fputs(s, fp);
996 shtree(n->nbinary.ch2, ind, NULL, fp);
997 break;
998 case NCMD:
999 shcmd(n, fp);
1000 if (ind >= 0)
1001 putc('\n', fp);
1002 break;
1003 case NPIPE:
1004 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
1005 shcmd(lp->n, fp);
1006 if (lp->next)
1007 fputs(" | ", fp);
1008 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001009 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001010 fputs(" &", fp);
1011 if (ind >= 0)
1012 putc('\n', fp);
1013 break;
1014 default:
1015 fprintf(fp, "<node type %d>", n->type);
1016 if (ind >= 0)
1017 putc('\n', fp);
1018 break;
1019 }
1020}
1021
1022static void
1023showtree(union node *n)
1024{
1025 trace_puts("showtree called\n");
1026 shtree(n, 1, NULL, stdout);
1027}
1028
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001029#endif /* DEBUG */
1030
1031
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001032/* ============ Parser data */
1033
1034/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001035 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1036 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001037struct strlist {
1038 struct strlist *next;
1039 char *text;
1040};
1041
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001042struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001043
Denis Vlasenkob012b102007-02-19 22:43:01 +00001044struct strpush {
1045 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001046 char *prev_string;
1047 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001048#if ENABLE_ASH_ALIAS
1049 struct alias *ap; /* if push was associated with an alias */
1050#endif
1051 char *string; /* remember the string since it may change */
1052};
1053
1054struct parsefile {
1055 struct parsefile *prev; /* preceding file on stack */
1056 int linno; /* current line */
1057 int fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001058 int left_in_line; /* number of chars left in this line */
1059 int left_in_buffer; /* number of chars left in this buffer past the line */
1060 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001061 char *buf; /* input buffer */
1062 struct strpush *strpush; /* for pushing strings at this level */
1063 struct strpush basestrpush; /* so pushing one is fast */
1064};
1065
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001066static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001067static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001068static int startlinno; /* line # where last token started */
1069static char *commandname; /* currently executing command */
1070static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001071static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001072
1073
1074/* ============ Message printing */
1075
1076static void
1077ash_vmsg(const char *msg, va_list ap)
1078{
1079 fprintf(stderr, "%s: ", arg0);
1080 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001081 if (strcmp(arg0, commandname))
1082 fprintf(stderr, "%s: ", commandname);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001083 if (!iflag || g_parsefile->fd)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001084 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001085 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001086 vfprintf(stderr, msg, ap);
1087 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001088}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001089
1090/*
1091 * Exverror is called to raise the error exception. If the second argument
1092 * is not NULL then error prints an error message using printf style
1093 * formatting. It then raises the error exception.
1094 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001095static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001096static void
1097ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001098{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001099#if DEBUG
1100 if (msg) {
1101 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1102 TRACEV((msg, ap));
1103 TRACE(("\") pid=%d\n", getpid()));
1104 } else
1105 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1106 if (msg)
1107#endif
1108 ash_vmsg(msg, ap);
1109
1110 flush_stdout_stderr();
1111 raise_exception(cond);
1112 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001113}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001114
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001115static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001116static void
1117ash_msg_and_raise_error(const char *msg, ...)
1118{
1119 va_list ap;
1120
1121 va_start(ap, msg);
1122 ash_vmsg_and_raise(EXERROR, msg, ap);
1123 /* NOTREACHED */
1124 va_end(ap);
1125}
1126
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001127static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001128static void
1129ash_msg_and_raise(int cond, const char *msg, ...)
1130{
1131 va_list ap;
1132
1133 va_start(ap, msg);
1134 ash_vmsg_and_raise(cond, msg, ap);
1135 /* NOTREACHED */
1136 va_end(ap);
1137}
1138
1139/*
1140 * error/warning routines for external builtins
1141 */
1142static void
1143ash_msg(const char *fmt, ...)
1144{
1145 va_list ap;
1146
1147 va_start(ap, fmt);
1148 ash_vmsg(fmt, ap);
1149 va_end(ap);
1150}
1151
1152/*
1153 * Return a string describing an error. The returned string may be a
1154 * pointer to a static buffer that will be overwritten on the next call.
1155 * Action describes the operation that got the error.
1156 */
1157static const char *
1158errmsg(int e, const char *em)
1159{
1160 if (e == ENOENT || e == ENOTDIR) {
1161 return em;
1162 }
1163 return strerror(e);
1164}
1165
1166
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001167/* ============ Memory allocation */
1168
1169/*
1170 * It appears that grabstackstr() will barf with such alignments
1171 * because stalloc() will return a string allocated in a new stackblock.
1172 */
1173#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1174enum {
1175 /* Most machines require the value returned from malloc to be aligned
1176 * in some way. The following macro will get this right
1177 * on many machines. */
1178 SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
1179 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001180 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001181};
1182
1183struct stack_block {
1184 struct stack_block *prev;
1185 char space[MINSIZE];
1186};
1187
1188struct stackmark {
1189 struct stack_block *stackp;
1190 char *stacknxt;
1191 size_t stacknleft;
1192 struct stackmark *marknext;
1193};
1194
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001195
Denis Vlasenko01631112007-12-16 17:20:38 +00001196struct globals_memstack {
1197 struct stack_block *g_stackp; // = &stackbase;
1198 struct stackmark *markp;
1199 char *g_stacknxt; // = stackbase.space;
1200 char *sstrend; // = stackbase.space + MINSIZE;
1201 size_t g_stacknleft; // = MINSIZE;
1202 int herefd; // = -1;
1203 struct stack_block stackbase;
1204};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001205extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1206#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001207#define g_stackp (G_memstack.g_stackp )
1208#define markp (G_memstack.markp )
1209#define g_stacknxt (G_memstack.g_stacknxt )
1210#define sstrend (G_memstack.sstrend )
1211#define g_stacknleft (G_memstack.g_stacknleft)
1212#define herefd (G_memstack.herefd )
1213#define stackbase (G_memstack.stackbase )
1214#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001215 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1216 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001217 g_stackp = &stackbase; \
1218 g_stacknxt = stackbase.space; \
1219 g_stacknleft = MINSIZE; \
1220 sstrend = stackbase.space + MINSIZE; \
1221 herefd = -1; \
1222} while (0)
1223
1224#define stackblock() ((void *)g_stacknxt)
1225#define stackblocksize() g_stacknleft
1226
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001227
1228static void *
1229ckrealloc(void * p, size_t nbytes)
1230{
1231 p = realloc(p, nbytes);
1232 if (!p)
1233 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1234 return p;
1235}
1236
1237static void *
1238ckmalloc(size_t nbytes)
1239{
1240 return ckrealloc(NULL, nbytes);
1241}
1242
Denis Vlasenko597906c2008-02-20 16:38:54 +00001243static void *
1244ckzalloc(size_t nbytes)
1245{
1246 return memset(ckmalloc(nbytes), 0, nbytes);
1247}
1248
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001249/*
1250 * Make a copy of a string in safe storage.
1251 */
1252static char *
1253ckstrdup(const char *s)
1254{
1255 char *p = strdup(s);
1256 if (!p)
1257 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1258 return p;
1259}
1260
1261/*
1262 * Parse trees for commands are allocated in lifo order, so we use a stack
1263 * to make this more efficient, and also to avoid all sorts of exception
1264 * handling code to handle interrupts in the middle of a parse.
1265 *
1266 * The size 504 was chosen because the Ultrix malloc handles that size
1267 * well.
1268 */
1269static void *
1270stalloc(size_t nbytes)
1271{
1272 char *p;
1273 size_t aligned;
1274
1275 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001276 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001277 size_t len;
1278 size_t blocksize;
1279 struct stack_block *sp;
1280
1281 blocksize = aligned;
1282 if (blocksize < MINSIZE)
1283 blocksize = MINSIZE;
1284 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1285 if (len < blocksize)
1286 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1287 INT_OFF;
1288 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001289 sp->prev = g_stackp;
1290 g_stacknxt = sp->space;
1291 g_stacknleft = blocksize;
1292 sstrend = g_stacknxt + blocksize;
1293 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001294 INT_ON;
1295 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001296 p = g_stacknxt;
1297 g_stacknxt += aligned;
1298 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001299 return p;
1300}
1301
Denis Vlasenko597906c2008-02-20 16:38:54 +00001302static void *
1303stzalloc(size_t nbytes)
1304{
1305 return memset(stalloc(nbytes), 0, nbytes);
1306}
1307
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001308static void
1309stunalloc(void *p)
1310{
1311#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001312 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001313 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001314 abort();
1315 }
1316#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001317 g_stacknleft += g_stacknxt - (char *)p;
1318 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001319}
1320
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001321/*
1322 * Like strdup but works with the ash stack.
1323 */
1324static char *
1325ststrdup(const char *p)
1326{
1327 size_t len = strlen(p) + 1;
1328 return memcpy(stalloc(len), p, len);
1329}
1330
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331static void
1332setstackmark(struct stackmark *mark)
1333{
Denis Vlasenko01631112007-12-16 17:20:38 +00001334 mark->stackp = g_stackp;
1335 mark->stacknxt = g_stacknxt;
1336 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001337 mark->marknext = markp;
1338 markp = mark;
1339}
1340
1341static void
1342popstackmark(struct stackmark *mark)
1343{
1344 struct stack_block *sp;
1345
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001346 if (!mark->stackp)
1347 return;
1348
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001349 INT_OFF;
1350 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001351 while (g_stackp != mark->stackp) {
1352 sp = g_stackp;
1353 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001354 free(sp);
1355 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001356 g_stacknxt = mark->stacknxt;
1357 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358 sstrend = mark->stacknxt + mark->stacknleft;
1359 INT_ON;
1360}
1361
1362/*
1363 * When the parser reads in a string, it wants to stick the string on the
1364 * stack and only adjust the stack pointer when it knows how big the
1365 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1366 * of space on top of the stack and stackblocklen returns the length of
1367 * this block. Growstackblock will grow this space by at least one byte,
1368 * possibly moving it (like realloc). Grabstackblock actually allocates the
1369 * part of the block that has been used.
1370 */
1371static void
1372growstackblock(void)
1373{
1374 size_t newlen;
1375
Denis Vlasenko01631112007-12-16 17:20:38 +00001376 newlen = g_stacknleft * 2;
1377 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001378 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1379 if (newlen < 128)
1380 newlen += 128;
1381
Denis Vlasenko01631112007-12-16 17:20:38 +00001382 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001383 struct stack_block *oldstackp;
1384 struct stackmark *xmark;
1385 struct stack_block *sp;
1386 struct stack_block *prevstackp;
1387 size_t grosslen;
1388
1389 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001390 oldstackp = g_stackp;
1391 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001392 prevstackp = sp->prev;
1393 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1394 sp = ckrealloc(sp, grosslen);
1395 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001396 g_stackp = sp;
1397 g_stacknxt = sp->space;
1398 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001399 sstrend = sp->space + newlen;
1400
1401 /*
1402 * Stack marks pointing to the start of the old block
1403 * must be relocated to point to the new block
1404 */
1405 xmark = markp;
1406 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001407 xmark->stackp = g_stackp;
1408 xmark->stacknxt = g_stacknxt;
1409 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001410 xmark = xmark->marknext;
1411 }
1412 INT_ON;
1413 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001414 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001415 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001416 char *p = stalloc(newlen);
1417
1418 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001419 g_stacknxt = memcpy(p, oldspace, oldlen);
1420 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001421 }
1422}
1423
1424static void
1425grabstackblock(size_t len)
1426{
1427 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001428 g_stacknxt += len;
1429 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001430}
1431
1432/*
1433 * The following routines are somewhat easier to use than the above.
1434 * The user declares a variable of type STACKSTR, which may be declared
1435 * to be a register. The macro STARTSTACKSTR initializes things. Then
1436 * the user uses the macro STPUTC to add characters to the string. In
1437 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1438 * grown as necessary. When the user is done, she can just leave the
1439 * string there and refer to it using stackblock(). Or she can allocate
1440 * the space for it using grabstackstr(). If it is necessary to allow
1441 * someone else to use the stack temporarily and then continue to grow
1442 * the string, the user should use grabstack to allocate the space, and
1443 * then call ungrabstr(p) to return to the previous mode of operation.
1444 *
1445 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1446 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1447 * is space for at least one character.
1448 */
1449static void *
1450growstackstr(void)
1451{
1452 size_t len = stackblocksize();
1453 if (herefd >= 0 && len >= 1024) {
1454 full_write(herefd, stackblock(), len);
1455 return stackblock();
1456 }
1457 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001458 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001459}
1460
1461/*
1462 * Called from CHECKSTRSPACE.
1463 */
1464static char *
1465makestrspace(size_t newlen, char *p)
1466{
Denis Vlasenko01631112007-12-16 17:20:38 +00001467 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001468 size_t size = stackblocksize();
1469
1470 for (;;) {
1471 size_t nleft;
1472
1473 size = stackblocksize();
1474 nleft = size - len;
1475 if (nleft >= newlen)
1476 break;
1477 growstackblock();
1478 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001479 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001480}
1481
1482static char *
1483stack_nputstr(const char *s, size_t n, char *p)
1484{
1485 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001486 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001487 return p;
1488}
1489
1490static char *
1491stack_putstr(const char *s, char *p)
1492{
1493 return stack_nputstr(s, strlen(s), p);
1494}
1495
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001496static char *
1497_STPUTC(int c, char *p)
1498{
1499 if (p == sstrend)
1500 p = growstackstr();
1501 *p++ = c;
1502 return p;
1503}
1504
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001505#define STARTSTACKSTR(p) ((p) = stackblock())
1506#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001507#define CHECKSTRSPACE(n, p) do { \
1508 char *q = (p); \
1509 size_t l = (n); \
1510 size_t m = sstrend - q; \
1511 if (l > m) \
1512 (p) = makestrspace(l, q); \
1513} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001514#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001515#define STACKSTRNUL(p) do { \
1516 if ((p) == sstrend) \
1517 (p) = growstackstr(); \
1518 *(p) = '\0'; \
1519} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001520#define STUNPUTC(p) (--(p))
1521#define STTOPC(p) ((p)[-1])
1522#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001523
1524#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001525#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001526#define stackstrend() ((void *)sstrend)
1527
1528
1529/* ============ String helpers */
1530
1531/*
1532 * prefix -- see if pfx is a prefix of string.
1533 */
1534static char *
1535prefix(const char *string, const char *pfx)
1536{
1537 while (*pfx) {
1538 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001539 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001540 }
1541 return (char *) string;
1542}
1543
1544/*
1545 * Check for a valid number. This should be elsewhere.
1546 */
1547static int
1548is_number(const char *p)
1549{
1550 do {
1551 if (!isdigit(*p))
1552 return 0;
1553 } while (*++p != '\0');
1554 return 1;
1555}
1556
1557/*
1558 * Convert a string of digits to an integer, printing an error message on
1559 * failure.
1560 */
1561static int
1562number(const char *s)
1563{
1564 if (!is_number(s))
1565 ash_msg_and_raise_error(illnum, s);
1566 return atoi(s);
1567}
1568
1569/*
1570 * Produce a possibly single quoted string suitable as input to the shell.
1571 * The return string is allocated on the stack.
1572 */
1573static char *
1574single_quote(const char *s)
1575{
1576 char *p;
1577
1578 STARTSTACKSTR(p);
1579
1580 do {
1581 char *q;
1582 size_t len;
1583
1584 len = strchrnul(s, '\'') - s;
1585
1586 q = p = makestrspace(len + 3, p);
1587
1588 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001589 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001590 *q++ = '\'';
1591 s += len;
1592
1593 STADJUST(q - p, p);
1594
1595 len = strspn(s, "'");
1596 if (!len)
1597 break;
1598
1599 q = p = makestrspace(len + 3, p);
1600
1601 *q++ = '"';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001602 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001603 *q++ = '"';
1604 s += len;
1605
1606 STADJUST(q - p, p);
1607 } while (*s);
1608
1609 USTPUTC(0, p);
1610
1611 return stackblock();
1612}
1613
1614
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001615/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001616
1617static char **argptr; /* argument list for builtin commands */
1618static char *optionarg; /* set by nextopt (like getopt) */
1619static char *optptr; /* used by nextopt */
1620
1621/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001622 * XXX - should get rid of. Have all builtins use getopt(3).
1623 * The library getopt must have the BSD extension static variable
1624 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001626 * Standard option processing (a la getopt) for builtin routines.
1627 * The only argument that is passed to nextopt is the option string;
1628 * the other arguments are unnecessary. It returns the character,
1629 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001630 */
1631static int
1632nextopt(const char *optstring)
1633{
1634 char *p;
1635 const char *q;
1636 char c;
1637
1638 p = optptr;
1639 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001640 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001641 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001642 if (p == NULL)
1643 return '\0';
1644 if (*p != '-')
1645 return '\0';
1646 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001647 return '\0';
1648 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001649 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001651 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001652 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001653 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001654 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001655 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001656 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001657 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001658 if (*++q == ':')
1659 q++;
1660 }
1661 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001662 if (*p == '\0') {
1663 p = *argptr++;
1664 if (p == NULL)
1665 ash_msg_and_raise_error("no arg for -%c option", c);
1666 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001667 optionarg = p;
1668 p = NULL;
1669 }
1670 optptr = p;
1671 return c;
1672}
1673
1674
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001675/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001676
Denis Vlasenko01631112007-12-16 17:20:38 +00001677/*
1678 * The parsefile structure pointed to by the global variable parsefile
1679 * contains information about the current file being read.
1680 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001681struct shparam {
1682 int nparam; /* # of positional parameters (without $0) */
1683#if ENABLE_ASH_GETOPTS
1684 int optind; /* next parameter to be processed by getopts */
1685 int optoff; /* used by getopts */
1686#endif
1687 unsigned char malloced; /* if parameter list dynamically allocated */
1688 char **p; /* parameter list */
1689};
1690
1691/*
1692 * Free the list of positional parameters.
1693 */
1694static void
1695freeparam(volatile struct shparam *param)
1696{
Denis Vlasenko01631112007-12-16 17:20:38 +00001697 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001698 char **ap, **ap1;
1699 ap = ap1 = param->p;
1700 while (*ap)
1701 free(*ap++);
1702 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001703 }
1704}
1705
1706#if ENABLE_ASH_GETOPTS
1707static void getoptsreset(const char *value);
1708#endif
1709
1710struct var {
1711 struct var *next; /* next entry in hash list */
1712 int flags; /* flags are defined above */
1713 const char *text; /* name=value */
1714 void (*func)(const char *); /* function to be called when */
1715 /* the variable gets set/unset */
1716};
1717
1718struct localvar {
1719 struct localvar *next; /* next local variable in list */
1720 struct var *vp; /* the variable that was made local */
1721 int flags; /* saved flags */
1722 const char *text; /* saved text */
1723};
1724
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001725/* flags */
1726#define VEXPORT 0x01 /* variable is exported */
1727#define VREADONLY 0x02 /* variable cannot be modified */
1728#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1729#define VTEXTFIXED 0x08 /* text is statically allocated */
1730#define VSTACK 0x10 /* text is allocated on the stack */
1731#define VUNSET 0x20 /* the variable is not set */
1732#define VNOFUNC 0x40 /* don't call the callback function */
1733#define VNOSET 0x80 /* do not set variable - just readonly test */
1734#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001735#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001736# define VDYNAMIC 0x200 /* dynamic variable */
1737#else
1738# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739#endif
1740
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001741#ifdef IFS_BROKEN
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001742static const char defifsvar[] ALIGN1 = "IFS= \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743#define defifs (defifsvar + 4)
1744#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00001745static const char defifs[] ALIGN1 = " \t\n";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746#endif
1747
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001748
Denis Vlasenko01631112007-12-16 17:20:38 +00001749/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001750#if ENABLE_LOCALE_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001751static void
1752change_lc_all(const char *value)
1753{
1754 if (value && *value != '\0')
1755 setlocale(LC_ALL, value);
1756}
1757static void
1758change_lc_ctype(const char *value)
1759{
1760 if (value && *value != '\0')
1761 setlocale(LC_CTYPE, value);
1762}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001763#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001764#if ENABLE_ASH_MAIL
1765static void chkmail(void);
1766static void changemail(const char *);
1767#endif
1768static void changepath(const char *);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001769#if ENABLE_ASH_RANDOM_SUPPORT
1770static void change_random(const char *);
1771#endif
1772
Denis Vlasenko01631112007-12-16 17:20:38 +00001773static const struct {
1774 int flags;
1775 const char *text;
1776 void (*func)(const char *);
1777} varinit_data[] = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001778#ifdef IFS_BROKEN
Denis Vlasenko01631112007-12-16 17:20:38 +00001779 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780#else
Denis Vlasenko01631112007-12-16 17:20:38 +00001781 { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783#if ENABLE_ASH_MAIL
Denis Vlasenko01631112007-12-16 17:20:38 +00001784 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
1785 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001787 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1788 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1789 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1790 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001792 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001793#endif
1794#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001795 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796#endif
1797#if ENABLE_LOCALE_SUPPORT
Denis Vlasenko01631112007-12-16 17:20:38 +00001798 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
1799 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800#endif
1801#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko01631112007-12-16 17:20:38 +00001802 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001803#endif
1804};
1805
Denis Vlasenko0b769642008-07-24 07:54:57 +00001806struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001807
1808struct globals_var {
1809 struct shparam shellparam; /* $@ current positional parameters */
1810 struct redirtab *redirlist;
1811 int g_nullredirs;
1812 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1813 struct var *vartab[VTABSIZE];
1814 struct var varinit[ARRAY_SIZE(varinit_data)];
1815};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001816extern struct globals_var *const ash_ptr_to_globals_var;
1817#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001818#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001819//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001820#define g_nullredirs (G_var.g_nullredirs )
1821#define preverrout_fd (G_var.preverrout_fd)
1822#define vartab (G_var.vartab )
1823#define varinit (G_var.varinit )
1824#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001825 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001826 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1827 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001828 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
1829 varinit[i].flags = varinit_data[i].flags; \
1830 varinit[i].text = varinit_data[i].text; \
1831 varinit[i].func = varinit_data[i].func; \
1832 } \
1833} while (0)
1834
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001835#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001836#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001837# define vmail (&vifs)[1]
1838# define vmpath (&vmail)[1]
1839# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001840#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001841# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001842#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001843#define vps1 (&vpath)[1]
1844#define vps2 (&vps1)[1]
1845#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001846#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001847# define voptind (&vps4)[1]
1848# if ENABLE_ASH_RANDOM_SUPPORT
1849# define vrandom (&voptind)[1]
1850# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001851#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001852# if ENABLE_ASH_RANDOM_SUPPORT
1853# define vrandom (&vps4)[1]
1854# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001855#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001856
1857/*
1858 * The following macros access the values of the above variables.
1859 * They have to skip over the name. They return the null string
1860 * for unset variables.
1861 */
1862#define ifsval() (vifs.text + 4)
1863#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001864#if ENABLE_ASH_MAIL
1865# define mailval() (vmail.text + 5)
1866# define mpathval() (vmpath.text + 9)
1867# define mpathset() ((vmpath.flags & VUNSET) == 0)
1868#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001869#define pathval() (vpath.text + 5)
1870#define ps1val() (vps1.text + 4)
1871#define ps2val() (vps2.text + 4)
1872#define ps4val() (vps4.text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001873#if ENABLE_ASH_GETOPTS
1874# define optindval() (voptind.text + 7)
1875#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001876
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001877
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001878#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1879#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1880
Denis Vlasenko01631112007-12-16 17:20:38 +00001881#if ENABLE_ASH_GETOPTS
1882static void
1883getoptsreset(const char *value)
1884{
1885 shellparam.optind = number(value);
1886 shellparam.optoff = -1;
1887}
1888#endif
1889
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890/*
1891 * Return of a legal variable name (a letter or underscore followed by zero or
1892 * more letters, underscores, and digits).
1893 */
1894static char *
1895endofname(const char *name)
1896{
1897 char *p;
1898
1899 p = (char *) name;
1900 if (!is_name(*p))
1901 return p;
1902 while (*++p) {
1903 if (!is_in_name(*p))
1904 break;
1905 }
1906 return p;
1907}
1908
1909/*
1910 * Compares two strings up to the first = or '\0'. The first
1911 * string must be terminated by '='; the second may be terminated by
1912 * either '=' or '\0'.
1913 */
1914static int
1915varcmp(const char *p, const char *q)
1916{
1917 int c, d;
1918
1919 while ((c = *p) == (d = *q)) {
1920 if (!c || c == '=')
1921 goto out;
1922 p++;
1923 q++;
1924 }
1925 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001926 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001927 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00001928 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001929 out:
1930 return c - d;
1931}
1932
1933static int
1934varequal(const char *a, const char *b)
1935{
1936 return !varcmp(a, b);
1937}
1938
1939/*
1940 * Find the appropriate entry in the hash table from the name.
1941 */
1942static struct var **
1943hashvar(const char *p)
1944{
1945 unsigned hashval;
1946
1947 hashval = ((unsigned char) *p) << 4;
1948 while (*p && *p != '=')
1949 hashval += (unsigned char) *p++;
1950 return &vartab[hashval % VTABSIZE];
1951}
1952
1953static int
1954vpcmp(const void *a, const void *b)
1955{
1956 return varcmp(*(const char **)a, *(const char **)b);
1957}
1958
1959/*
1960 * This routine initializes the builtin variables.
1961 */
1962static void
1963initvar(void)
1964{
1965 struct var *vp;
1966 struct var *end;
1967 struct var **vpp;
1968
1969 /*
1970 * PS1 depends on uid
1971 */
1972#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
1973 vps1.text = "PS1=\\w \\$ ";
1974#else
1975 if (!geteuid())
1976 vps1.text = "PS1=# ";
1977#endif
1978 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00001979 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980 do {
1981 vpp = hashvar(vp->text);
1982 vp->next = *vpp;
1983 *vpp = vp;
1984 } while (++vp < end);
1985}
1986
1987static struct var **
1988findvar(struct var **vpp, const char *name)
1989{
1990 for (; *vpp; vpp = &(*vpp)->next) {
1991 if (varequal((*vpp)->text, name)) {
1992 break;
1993 }
1994 }
1995 return vpp;
1996}
1997
1998/*
1999 * Find the value of a variable. Returns NULL if not set.
2000 */
2001static char *
2002lookupvar(const char *name)
2003{
2004 struct var *v;
2005
2006 v = *findvar(hashvar(name), name);
2007 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002008#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002009 /*
2010 * Dynamic variables are implemented roughly the same way they are
2011 * in bash. Namely, they're "special" so long as they aren't unset.
2012 * As soon as they're unset, they're no longer dynamic, and dynamic
2013 * lookup will no longer happen at that point. -- PFM.
2014 */
2015 if ((v->flags & VDYNAMIC))
2016 (*v->func)(NULL);
2017#endif
2018 if (!(v->flags & VUNSET))
2019 return strchrnul(v->text, '=') + 1;
2020 }
2021 return NULL;
2022}
2023
2024/*
2025 * Search the environment of a builtin command.
2026 */
2027static char *
2028bltinlookup(const char *name)
2029{
2030 struct strlist *sp;
2031
2032 for (sp = cmdenviron; sp; sp = sp->next) {
2033 if (varequal(sp->text, name))
2034 return strchrnul(sp->text, '=') + 1;
2035 }
2036 return lookupvar(name);
2037}
2038
2039/*
2040 * Same as setvar except that the variable and value are passed in
2041 * the first argument as name=value. Since the first argument will
2042 * be actually stored in the table, it should not be a string that
2043 * will go away.
2044 * Called with interrupts off.
2045 */
2046static void
2047setvareq(char *s, int flags)
2048{
2049 struct var *vp, **vpp;
2050
2051 vpp = hashvar(s);
2052 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2053 vp = *findvar(vpp, s);
2054 if (vp) {
2055 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2056 const char *n;
2057
2058 if (flags & VNOSAVE)
2059 free(s);
2060 n = vp->text;
2061 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2062 }
2063
2064 if (flags & VNOSET)
2065 return;
2066
2067 if (vp->func && (flags & VNOFUNC) == 0)
2068 (*vp->func)(strchrnul(s, '=') + 1);
2069
2070 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2071 free((char*)vp->text);
2072
2073 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2074 } else {
2075 if (flags & VNOSET)
2076 return;
2077 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00002078 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002080 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002081 *vpp = vp;
2082 }
2083 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2084 s = ckstrdup(s);
2085 vp->text = s;
2086 vp->flags = flags;
2087}
2088
2089/*
2090 * Set the value of a variable. The flags argument is ored with the
2091 * flags of the variable. If val is NULL, the variable is unset.
2092 */
2093static void
2094setvar(const char *name, const char *val, int flags)
2095{
2096 char *p, *q;
2097 size_t namelen;
2098 char *nameeq;
2099 size_t vallen;
2100
2101 q = endofname(name);
2102 p = strchrnul(q, '=');
2103 namelen = p - name;
2104 if (!namelen || p != q)
2105 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2106 vallen = 0;
2107 if (val == NULL) {
2108 flags |= VUNSET;
2109 } else {
2110 vallen = strlen(val);
2111 }
2112 INT_OFF;
2113 nameeq = ckmalloc(namelen + vallen + 2);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002114 p = (char *)memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002115 if (val) {
2116 *p++ = '=';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002117 p = (char *)memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002118 }
2119 *p = '\0';
2120 setvareq(nameeq, flags | VNOSAVE);
2121 INT_ON;
2122}
2123
2124#if ENABLE_ASH_GETOPTS
2125/*
2126 * Safe version of setvar, returns 1 on success 0 on failure.
2127 */
2128static int
2129setvarsafe(const char *name, const char *val, int flags)
2130{
2131 int err;
2132 volatile int saveint;
2133 struct jmploc *volatile savehandler = exception_handler;
2134 struct jmploc jmploc;
2135
2136 SAVE_INT(saveint);
2137 if (setjmp(jmploc.loc))
2138 err = 1;
2139 else {
2140 exception_handler = &jmploc;
2141 setvar(name, val, flags);
2142 err = 0;
2143 }
2144 exception_handler = savehandler;
2145 RESTORE_INT(saveint);
2146 return err;
2147}
2148#endif
2149
2150/*
2151 * Unset the specified variable.
2152 */
2153static int
2154unsetvar(const char *s)
2155{
2156 struct var **vpp;
2157 struct var *vp;
2158 int retval;
2159
2160 vpp = findvar(hashvar(s), s);
2161 vp = *vpp;
2162 retval = 2;
2163 if (vp) {
2164 int flags = vp->flags;
2165
2166 retval = 1;
2167 if (flags & VREADONLY)
2168 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002169#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002170 vp->flags &= ~VDYNAMIC;
2171#endif
2172 if (flags & VUNSET)
2173 goto ok;
2174 if ((flags & VSTRFIXED) == 0) {
2175 INT_OFF;
2176 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
2177 free((char*)vp->text);
2178 *vpp = vp->next;
2179 free(vp);
2180 INT_ON;
2181 } else {
2182 setvar(s, 0, 0);
2183 vp->flags &= ~VEXPORT;
2184 }
2185 ok:
2186 retval = 0;
2187 }
2188 out:
2189 return retval;
2190}
2191
2192/*
2193 * Process a linked list of variable assignments.
2194 */
2195static void
2196listsetvar(struct strlist *list_set_var, int flags)
2197{
2198 struct strlist *lp = list_set_var;
2199
2200 if (!lp)
2201 return;
2202 INT_OFF;
2203 do {
2204 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002205 lp = lp->next;
2206 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002207 INT_ON;
2208}
2209
2210/*
2211 * Generate a list of variables satisfying the given conditions.
2212 */
2213static char **
2214listvars(int on, int off, char ***end)
2215{
2216 struct var **vpp;
2217 struct var *vp;
2218 char **ep;
2219 int mask;
2220
2221 STARTSTACKSTR(ep);
2222 vpp = vartab;
2223 mask = on | off;
2224 do {
2225 for (vp = *vpp; vp; vp = vp->next) {
2226 if ((vp->flags & mask) == on) {
2227 if (ep == stackstrend())
2228 ep = growstackstr();
2229 *ep++ = (char *) vp->text;
2230 }
2231 }
2232 } while (++vpp < vartab + VTABSIZE);
2233 if (ep == stackstrend())
2234 ep = growstackstr();
2235 if (end)
2236 *end = ep;
2237 *ep++ = NULL;
2238 return grabstackstr(ep);
2239}
2240
2241
2242/* ============ Path search helper
2243 *
2244 * The variable path (passed by reference) should be set to the start
2245 * of the path before the first call; padvance will update
2246 * this value as it proceeds. Successive calls to padvance will return
2247 * the possible path expansions in sequence. If an option (indicated by
2248 * a percent sign) appears in the path entry then the global variable
2249 * pathopt will be set to point to it; otherwise pathopt will be set to
2250 * NULL.
2251 */
2252static const char *pathopt; /* set by padvance */
2253
2254static char *
2255padvance(const char **path, const char *name)
2256{
2257 const char *p;
2258 char *q;
2259 const char *start;
2260 size_t len;
2261
2262 if (*path == NULL)
2263 return NULL;
2264 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002265 for (p = start; *p && *p != ':' && *p != '%'; p++)
2266 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002267 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2268 while (stackblocksize() < len)
2269 growstackblock();
2270 q = stackblock();
2271 if (p != start) {
2272 memcpy(q, start, p - start);
2273 q += p - start;
2274 *q++ = '/';
2275 }
2276 strcpy(q, name);
2277 pathopt = NULL;
2278 if (*p == '%') {
2279 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002280 while (*p && *p != ':')
2281 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002282 }
2283 if (*p == ':')
2284 *path = p + 1;
2285 else
2286 *path = NULL;
2287 return stalloc(len);
2288}
2289
2290
2291/* ============ Prompt */
2292
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002293static smallint doprompt; /* if set, prompt the user */
2294static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002295
2296#if ENABLE_FEATURE_EDITING
2297static line_input_t *line_input_state;
2298static const char *cmdedit_prompt;
2299static void
2300putprompt(const char *s)
2301{
2302 if (ENABLE_ASH_EXPAND_PRMT) {
2303 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002304 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002305 return;
2306 }
2307 cmdedit_prompt = s;
2308}
2309#else
2310static void
2311putprompt(const char *s)
2312{
2313 out2str(s);
2314}
2315#endif
2316
2317#if ENABLE_ASH_EXPAND_PRMT
2318/* expandstr() needs parsing machinery, so it is far away ahead... */
2319static const char *expandstr(const char *ps);
2320#else
2321#define expandstr(s) s
2322#endif
2323
2324static void
2325setprompt(int whichprompt)
2326{
2327 const char *prompt;
2328#if ENABLE_ASH_EXPAND_PRMT
2329 struct stackmark smark;
2330#endif
2331
2332 needprompt = 0;
2333
2334 switch (whichprompt) {
2335 case 1:
2336 prompt = ps1val();
2337 break;
2338 case 2:
2339 prompt = ps2val();
2340 break;
2341 default: /* 0 */
2342 prompt = nullstr;
2343 }
2344#if ENABLE_ASH_EXPAND_PRMT
2345 setstackmark(&smark);
2346 stalloc(stackblocksize());
2347#endif
2348 putprompt(expandstr(prompt));
2349#if ENABLE_ASH_EXPAND_PRMT
2350 popstackmark(&smark);
2351#endif
2352}
2353
2354
2355/* ============ The cd and pwd commands */
2356
2357#define CD_PHYSICAL 1
2358#define CD_PRINT 2
2359
2360static int docd(const char *, int);
2361
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002362static int
2363cdopt(void)
2364{
2365 int flags = 0;
2366 int i, j;
2367
2368 j = 'L';
2369 while ((i = nextopt("LP"))) {
2370 if (i != j) {
2371 flags ^= CD_PHYSICAL;
2372 j = i;
2373 }
2374 }
2375
2376 return flags;
2377}
2378
2379/*
2380 * Update curdir (the name of the current directory) in response to a
2381 * cd command.
2382 */
2383static const char *
2384updatepwd(const char *dir)
2385{
2386 char *new;
2387 char *p;
2388 char *cdcomppath;
2389 const char *lim;
2390
2391 cdcomppath = ststrdup(dir);
2392 STARTSTACKSTR(new);
2393 if (*dir != '/') {
2394 if (curdir == nullstr)
2395 return 0;
2396 new = stack_putstr(curdir, new);
2397 }
2398 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002399 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002400 if (*dir != '/') {
2401 if (new[-1] != '/')
2402 USTPUTC('/', new);
2403 if (new > lim && *lim == '/')
2404 lim++;
2405 } else {
2406 USTPUTC('/', new);
2407 cdcomppath++;
2408 if (dir[1] == '/' && dir[2] != '/') {
2409 USTPUTC('/', new);
2410 cdcomppath++;
2411 lim++;
2412 }
2413 }
2414 p = strtok(cdcomppath, "/");
2415 while (p) {
2416 switch (*p) {
2417 case '.':
2418 if (p[1] == '.' && p[2] == '\0') {
2419 while (new > lim) {
2420 STUNPUTC(new);
2421 if (new[-1] == '/')
2422 break;
2423 }
2424 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002425 }
2426 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002427 break;
2428 /* fall through */
2429 default:
2430 new = stack_putstr(p, new);
2431 USTPUTC('/', new);
2432 }
2433 p = strtok(0, "/");
2434 }
2435 if (new > lim)
2436 STUNPUTC(new);
2437 *new = 0;
2438 return stackblock();
2439}
2440
2441/*
2442 * Find out what the current directory is. If we already know the current
2443 * directory, this routine returns immediately.
2444 */
2445static char *
2446getpwd(void)
2447{
Denis Vlasenko01631112007-12-16 17:20:38 +00002448 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002449 return dir ? dir : nullstr;
2450}
2451
2452static void
2453setpwd(const char *val, int setold)
2454{
2455 char *oldcur, *dir;
2456
2457 oldcur = dir = curdir;
2458
2459 if (setold) {
2460 setvar("OLDPWD", oldcur, VEXPORT);
2461 }
2462 INT_OFF;
2463 if (physdir != nullstr) {
2464 if (physdir != oldcur)
2465 free(physdir);
2466 physdir = nullstr;
2467 }
2468 if (oldcur == val || !val) {
2469 char *s = getpwd();
2470 physdir = s;
2471 if (!val)
2472 dir = s;
2473 } else
2474 dir = ckstrdup(val);
2475 if (oldcur != dir && oldcur != nullstr) {
2476 free(oldcur);
2477 }
2478 curdir = dir;
2479 INT_ON;
2480 setvar("PWD", dir, VEXPORT);
2481}
2482
2483static void hashcd(void);
2484
2485/*
2486 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2487 * know that the current directory has changed.
2488 */
2489static int
2490docd(const char *dest, int flags)
2491{
2492 const char *dir = 0;
2493 int err;
2494
2495 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2496
2497 INT_OFF;
2498 if (!(flags & CD_PHYSICAL)) {
2499 dir = updatepwd(dest);
2500 if (dir)
2501 dest = dir;
2502 }
2503 err = chdir(dest);
2504 if (err)
2505 goto out;
2506 setpwd(dir, 1);
2507 hashcd();
2508 out:
2509 INT_ON;
2510 return err;
2511}
2512
2513static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002514cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002515{
2516 const char *dest;
2517 const char *path;
2518 const char *p;
2519 char c;
2520 struct stat statb;
2521 int flags;
2522
2523 flags = cdopt();
2524 dest = *argptr;
2525 if (!dest)
2526 dest = bltinlookup(homestr);
2527 else if (LONE_DASH(dest)) {
2528 dest = bltinlookup("OLDPWD");
2529 flags |= CD_PRINT;
2530 }
2531 if (!dest)
2532 dest = nullstr;
2533 if (*dest == '/')
2534 goto step7;
2535 if (*dest == '.') {
2536 c = dest[1];
2537 dotdot:
2538 switch (c) {
2539 case '\0':
2540 case '/':
2541 goto step6;
2542 case '.':
2543 c = dest[2];
2544 if (c != '.')
2545 goto dotdot;
2546 }
2547 }
2548 if (!*dest)
2549 dest = ".";
2550 path = bltinlookup("CDPATH");
2551 if (!path) {
2552 step6:
2553 step7:
2554 p = dest;
2555 goto docd;
2556 }
2557 do {
2558 c = *path;
2559 p = padvance(&path, dest);
2560 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2561 if (c && c != ':')
2562 flags |= CD_PRINT;
2563 docd:
2564 if (!docd(p, flags))
2565 goto out;
2566 break;
2567 }
2568 } while (path);
2569 ash_msg_and_raise_error("can't cd to %s", dest);
2570 /* NOTREACHED */
2571 out:
2572 if (flags & CD_PRINT)
2573 out1fmt(snlfmt, curdir);
2574 return 0;
2575}
2576
2577static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002578pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002579{
2580 int flags;
2581 const char *dir = curdir;
2582
2583 flags = cdopt();
2584 if (flags) {
2585 if (physdir == nullstr)
2586 setpwd(dir, 0);
2587 dir = physdir;
2588 }
2589 out1fmt(snlfmt, dir);
2590 return 0;
2591}
2592
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002593
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002594/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002595
Denis Vlasenko834dee72008-10-07 09:18:30 +00002596
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002597#define IBUFSIZ COMMON_BUFSIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00002598/* buffer for top level input file */
2599#define basebuf bb_common_bufsiz1
Eric Andersenc470f442003-07-28 09:56:35 +00002600
Eric Andersenc470f442003-07-28 09:56:35 +00002601/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002602#define CWORD 0 /* character is nothing special */
2603#define CNL 1 /* newline character */
2604#define CBACK 2 /* a backslash character */
2605#define CSQUOTE 3 /* single quote */
2606#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002607#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002608#define CBQUOTE 6 /* backwards single quote */
2609#define CVAR 7 /* a dollar sign */
2610#define CENDVAR 8 /* a '}' character */
2611#define CLP 9 /* a left paren in arithmetic */
2612#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002613#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002614#define CCTL 12 /* like CWORD, except it must be escaped */
2615#define CSPCL 13 /* these terminate a word */
2616#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002617
Denis Vlasenko131ae172007-02-18 13:00:19 +00002618#if ENABLE_ASH_ALIAS
Denis Vlasenko834dee72008-10-07 09:18:30 +00002619#define SYNBASE 130
2620#define PEOF -130
2621#define PEOA -129
Eric Andersenc470f442003-07-28 09:56:35 +00002622#define PEOA_OR_PEOF PEOA
2623#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00002624#define SYNBASE 129
2625#define PEOF -129
Eric Andersenc470f442003-07-28 09:56:35 +00002626#define PEOA_OR_PEOF PEOF
2627#endif
2628
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002629/* number syntax index */
2630#define BASESYNTAX 0 /* not in quotes */
2631#define DQSYNTAX 1 /* in double quotes */
2632#define SQSYNTAX 2 /* in single quotes */
2633#define ARISYNTAX 3 /* in arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +00002634#define PSSYNTAX 4 /* prompt */
Eric Andersenc470f442003-07-28 09:56:35 +00002635
Denis Vlasenko131ae172007-02-18 13:00:19 +00002636#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002637#define USE_SIT_FUNCTION
2638#endif
2639
Denis Vlasenko131ae172007-02-18 13:00:19 +00002640#if ENABLE_ASH_MATH_SUPPORT
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002641static const char S_I_T[][4] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002642#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002643 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002644#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002645 { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
2646 { CNL, CNL, CNL, CNL }, /* 2, \n */
2647 { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
2648 { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
2649 { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
2650 { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
2651 { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
2652 { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
2653 { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
2654 { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
2655 { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002656#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002657 { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2658 { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2659 { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002660#endif
Eric Andersen2870d962001-07-02 17:27:21 +00002661};
Eric Andersenc470f442003-07-28 09:56:35 +00002662#else
2663static const char S_I_T[][3] = {
Denis Vlasenko131ae172007-02-18 13:00:19 +00002664#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002665 { CSPCL, CIGN, CIGN }, /* 0, PEOA */
Eric Andersenc470f442003-07-28 09:56:35 +00002666#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002667 { CSPCL, CWORD, CWORD }, /* 1, ' ' */
2668 { CNL, CNL, CNL }, /* 2, \n */
2669 { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
2670 { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
2671 { CVAR, CVAR, CWORD }, /* 5, $ */
2672 { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
2673 { CSPCL, CWORD, CWORD }, /* 7, ( */
2674 { CSPCL, CWORD, CWORD }, /* 8, ) */
2675 { CBACK, CBACK, CCTL }, /* 9, \ */
2676 { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
2677 { CENDVAR, CENDVAR, CWORD }, /* 11, } */
Eric Andersenc470f442003-07-28 09:56:35 +00002678#ifndef USE_SIT_FUNCTION
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002679 { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
2680 { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
2681 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002682#endif
2683};
Denis Vlasenko131ae172007-02-18 13:00:19 +00002684#endif /* ASH_MATH_SUPPORT */
Eric Andersen2870d962001-07-02 17:27:21 +00002685
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002686#ifdef USE_SIT_FUNCTION
2687
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002688static int
2689SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002690{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002691 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denis Vlasenko131ae172007-02-18 13:00:19 +00002692#if ENABLE_ASH_ALIAS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002693 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002694 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2695 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2696 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2697 11, 3 /* "}~" */
2698 };
2699#else
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002700 static const char syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002701 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2702 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2703 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2704 10, 2 /* "}~" */
2705 };
2706#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002707 const char *s;
2708 int indx;
2709
Eric Andersenc470f442003-07-28 09:56:35 +00002710 if (c == PEOF) /* 2^8+2 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002711 return CENDFILE;
Denis Vlasenko131ae172007-02-18 13:00:19 +00002712#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002713 if (c == PEOA) /* 2^8+1 */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002714 indx = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00002715 else
2716#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002717
2718 if ((unsigned char)c >= (unsigned char)(CTLESC)
2719 && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
2720 ) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +00002721 return CCTL;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002722 }
Denis Vlasenko68819d12008-12-15 11:26:36 +00002723 s = strchrnul(spec_symbls, c);
2724 if (*s == '\0')
2725 return CWORD;
2726 indx = syntax_index_table[s - spec_symbls];
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002727 return S_I_T[indx][syntax];
2728}
2729
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002730#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002731
Denis Vlasenko131ae172007-02-18 13:00:19 +00002732#if ENABLE_ASH_ALIAS
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002733#define CSPCL_CIGN_CIGN_CIGN 0
2734#define CSPCL_CWORD_CWORD_CWORD 1
2735#define CNL_CNL_CNL_CNL 2
2736#define CWORD_CCTL_CCTL_CWORD 3
2737#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
2738#define CVAR_CVAR_CWORD_CVAR 5
2739#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
2740#define CSPCL_CWORD_CWORD_CLP 7
2741#define CSPCL_CWORD_CWORD_CRP 8
2742#define CBACK_CBACK_CCTL_CBACK 9
2743#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
2744#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
2745#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
2746#define CWORD_CWORD_CWORD_CWORD 13
2747#define CCTL_CCTL_CCTL_CCTL 14
Eric Andersenc470f442003-07-28 09:56:35 +00002748#else
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002749#define CSPCL_CWORD_CWORD_CWORD 0
2750#define CNL_CNL_CNL_CNL 1
2751#define CWORD_CCTL_CCTL_CWORD 2
2752#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
2753#define CVAR_CVAR_CWORD_CVAR 4
2754#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
2755#define CSPCL_CWORD_CWORD_CLP 6
2756#define CSPCL_CWORD_CWORD_CRP 7
2757#define CBACK_CBACK_CCTL_CBACK 8
2758#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
2759#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
2760#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
2761#define CWORD_CWORD_CWORD_CWORD 12
2762#define CCTL_CCTL_CCTL_CCTL 13
Eric Andersenc470f442003-07-28 09:56:35 +00002763#endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002764
2765static const char syntax_index_table[258] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002766 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Eric Andersenc470f442003-07-28 09:56:35 +00002767 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denis Vlasenko131ae172007-02-18 13:00:19 +00002768#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +00002769 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
2770#endif
2771 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2772 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2773 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2774 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2775 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2776 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2777 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2778 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2779 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002780 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
2781 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
2782 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
2783 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
2784 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
2785 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
2786 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
2787 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
2788 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
2789 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
2790 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
2791 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
2792 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
2793 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
2794 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
2795 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
2796 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
2797 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
2798 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
2799 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
2800 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
2801 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
2802 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
2803 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
2804 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
2805 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
2806 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
2807 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
2808 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
2809 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
2810 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
2811 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
2812 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
2813 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
2814 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
2815 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
2816 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
2817 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
2818 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
2819 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
2820 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
2821 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
2822 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
2823 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
2824 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
2825 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
2826 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
2827 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
2828 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
2829 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
2830 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
2831 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
2832 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
2833 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
2834 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
2835 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
2836 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
2837 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
2838 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
2889 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
2890 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
2891 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
2892 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
2893 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
2894 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
2895 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
2896 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
2897 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
2898 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
2899 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
2900 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
2901 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
2902 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
2903 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
2904 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
2905 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
2906 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
2907 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
2908 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2909 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
2910 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
2911 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
2912 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
2913 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
2914 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
2915 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
2916 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
2917 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
2918 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
2919 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
2920 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
2921 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
2922 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
2923 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
2924 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
2925 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
2926 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
2927 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
2928 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
2929 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
2930 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
2931 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2932 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002933 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002934 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2936 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
Eric Andersenc470f442003-07-28 09:56:35 +00002938 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002939 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2940 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2941 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2942 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
2944 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2945 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
2946 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2947 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2958 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2959 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2960 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2961 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2962 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2963 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2981 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2982 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2983 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2984 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2985 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2986 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2987 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2988 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2989 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2990 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2991 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2992 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2993 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2994 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2995 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2996 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2997 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2998 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2999 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3000 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3001 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3002 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3003 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3004 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3005 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3006 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3007 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3008 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3009 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3010 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3011 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3012 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3013 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3014 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3015 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3016 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3017 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3018 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3019 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3020 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3021 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3022 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3023 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3024 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3025 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3026 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
Eric Andersen2870d962001-07-02 17:27:21 +00003027};
3028
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003029#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
3030
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00003031#endif /* USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003032
Eric Andersen2870d962001-07-02 17:27:21 +00003033
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003034/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003035
Denis Vlasenko131ae172007-02-18 13:00:19 +00003036#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003037
3038#define ALIASINUSE 1
3039#define ALIASDEAD 2
3040
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003041struct alias {
3042 struct alias *next;
3043 char *name;
3044 char *val;
3045 int flag;
3046};
3047
Denis Vlasenko01631112007-12-16 17:20:38 +00003048
3049static struct alias **atab; // [ATABSIZE];
3050#define INIT_G_alias() do { \
3051 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3052} while (0)
3053
Eric Andersen2870d962001-07-02 17:27:21 +00003054
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003055static struct alias **
3056__lookupalias(const char *name) {
3057 unsigned int hashval;
3058 struct alias **app;
3059 const char *p;
3060 unsigned int ch;
3061
3062 p = name;
3063
3064 ch = (unsigned char)*p;
3065 hashval = ch << 4;
3066 while (ch) {
3067 hashval += ch;
3068 ch = (unsigned char)*++p;
3069 }
3070 app = &atab[hashval % ATABSIZE];
3071
3072 for (; *app; app = &(*app)->next) {
3073 if (strcmp(name, (*app)->name) == 0) {
3074 break;
3075 }
3076 }
3077
3078 return app;
3079}
3080
3081static struct alias *
3082lookupalias(const char *name, int check)
3083{
3084 struct alias *ap = *__lookupalias(name);
3085
3086 if (check && ap && (ap->flag & ALIASINUSE))
3087 return NULL;
3088 return ap;
3089}
3090
3091static struct alias *
3092freealias(struct alias *ap)
3093{
3094 struct alias *next;
3095
3096 if (ap->flag & ALIASINUSE) {
3097 ap->flag |= ALIASDEAD;
3098 return ap;
3099 }
3100
3101 next = ap->next;
3102 free(ap->name);
3103 free(ap->val);
3104 free(ap);
3105 return next;
3106}
Eric Andersencb57d552001-06-28 07:25:16 +00003107
Eric Andersenc470f442003-07-28 09:56:35 +00003108static void
3109setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003110{
3111 struct alias *ap, **app;
3112
3113 app = __lookupalias(name);
3114 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003115 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003116 if (ap) {
3117 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003118 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003119 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003120 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003121 ap->flag &= ~ALIASDEAD;
3122 } else {
3123 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003124 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003125 ap->name = ckstrdup(name);
3126 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003127 /*ap->flag = 0; - ckzalloc did it */
3128 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003129 *app = ap;
3130 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003131 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003132}
3133
Eric Andersenc470f442003-07-28 09:56:35 +00003134static int
3135unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003136{
Eric Andersencb57d552001-06-28 07:25:16 +00003137 struct alias **app;
3138
3139 app = __lookupalias(name);
3140
3141 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003142 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003143 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003144 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003145 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003146 }
3147
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003148 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003149}
3150
Eric Andersenc470f442003-07-28 09:56:35 +00003151static void
3152rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003153{
Eric Andersencb57d552001-06-28 07:25:16 +00003154 struct alias *ap, **app;
3155 int i;
3156
Denis Vlasenkob012b102007-02-19 22:43:01 +00003157 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003158 for (i = 0; i < ATABSIZE; i++) {
3159 app = &atab[i];
3160 for (ap = *app; ap; ap = *app) {
3161 *app = freealias(*app);
3162 if (ap == *app) {
3163 app = &ap->next;
3164 }
3165 }
3166 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003167 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003168}
3169
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003170static void
3171printalias(const struct alias *ap)
3172{
3173 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3174}
3175
Eric Andersencb57d552001-06-28 07:25:16 +00003176/*
3177 * TODO - sort output
3178 */
Eric Andersenc470f442003-07-28 09:56:35 +00003179static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003180aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003181{
3182 char *n, *v;
3183 int ret = 0;
3184 struct alias *ap;
3185
Denis Vlasenko68404f12008-03-17 09:00:54 +00003186 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003187 int i;
3188
Denis Vlasenko68404f12008-03-17 09:00:54 +00003189 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003190 for (ap = atab[i]; ap; ap = ap->next) {
3191 printalias(ap);
3192 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003193 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003194 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003195 }
3196 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003197 v = strchr(n+1, '=');
3198 if (v == NULL) { /* n+1: funny ksh stuff */
3199 ap = *__lookupalias(n);
3200 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003201 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003202 ret = 1;
3203 } else
3204 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003205 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003206 *v++ = '\0';
3207 setalias(n, v);
3208 }
3209 }
3210
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003211 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003212}
3213
Eric Andersenc470f442003-07-28 09:56:35 +00003214static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003215unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003216{
3217 int i;
3218
3219 while ((i = nextopt("a")) != '\0') {
3220 if (i == 'a') {
3221 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003222 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003223 }
3224 }
3225 for (i = 0; *argptr; argptr++) {
3226 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003227 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003228 i = 1;
3229 }
3230 }
3231
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003232 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003233}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003234
Denis Vlasenko131ae172007-02-18 13:00:19 +00003235#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003236
Eric Andersenc470f442003-07-28 09:56:35 +00003237
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003238/* ============ jobs.c */
3239
3240/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3241#define FORK_FG 0
3242#define FORK_BG 1
3243#define FORK_NOJOB 2
3244
3245/* mode flags for showjob(s) */
3246#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
3247#define SHOW_PID 0x04 /* include process pid */
3248#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
3249
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003250/*
3251 * A job structure contains information about a job. A job is either a
3252 * single process or a set of processes contained in a pipeline. In the
3253 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3254 * array of pids.
3255 */
3256
3257struct procstat {
3258 pid_t pid; /* process id */
3259 int status; /* last process status from wait() */
3260 char *cmd; /* text of command being run */
3261};
3262
3263struct job {
3264 struct procstat ps0; /* status of process */
3265 struct procstat *ps; /* status or processes when more than one */
3266#if JOBS
3267 int stopstatus; /* status of a stopped job */
3268#endif
3269 uint32_t
3270 nprocs: 16, /* number of processes */
3271 state: 8,
3272#define JOBRUNNING 0 /* at least one proc running */
3273#define JOBSTOPPED 1 /* all procs are stopped */
3274#define JOBDONE 2 /* all procs are completed */
3275#if JOBS
3276 sigint: 1, /* job was killed by SIGINT */
3277 jobctl: 1, /* job running under job control */
3278#endif
3279 waited: 1, /* true if this entry has been waited for */
3280 used: 1, /* true if this entry is in used */
3281 changed: 1; /* true if status has changed */
3282 struct job *prev_job; /* previous job */
3283};
3284
Denis Vlasenko68404f12008-03-17 09:00:54 +00003285static struct job *makejob(/*union node *,*/ int);
Denis Vlasenko85c24712008-03-17 09:04:04 +00003286#if !JOBS
3287#define forkshell(job, node, mode) forkshell(job, mode)
3288#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003289static int forkshell(struct job *, union node *, int);
3290static int waitforjob(struct job *);
3291
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003292#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003293enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003294#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003295#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003296static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003297static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003298#endif
3299
3300/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003301 * Ignore a signal.
3302 */
3303static void
3304ignoresig(int signo)
3305{
3306 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3307 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3308 /* No, need to do it */
3309 signal(signo, SIG_IGN);
3310 }
3311 sigmode[signo - 1] = S_HARD_IGN;
3312}
3313
3314/*
3315 * Signal handler. Only one usage site - in setsignal()
3316 */
3317static void
3318onsig(int signo)
3319{
3320 gotsig[signo - 1] = 1;
3321
3322 if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
3323 if (!suppressint) {
3324 pendingsig = 0;
3325 raise_interrupt(); /* does not return */
3326 }
3327 intpending = 1;
3328 } else {
3329 pendingsig = signo;
3330 }
3331}
3332
3333/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003334 * Set the signal handler for the specified signal. The routine figures
3335 * out what it should be set to.
3336 */
3337static void
3338setsignal(int signo)
3339{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003340 char *t;
3341 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003342 struct sigaction act;
3343
3344 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003345 new_act = S_DFL;
3346 if (t != NULL) { /* trap for this sig is set */
3347 new_act = S_CATCH;
3348 if (t[0] == '\0') /* trap is "": ignore this sig */
3349 new_act = S_IGN;
3350 }
3351
3352 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003353 switch (signo) {
3354 case SIGINT:
3355 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003356 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003357 break;
3358 case SIGQUIT:
3359#if DEBUG
3360 if (debug)
3361 break;
3362#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003363 /* man bash:
3364 * "In all cases, bash ignores SIGQUIT. Non-builtin
3365 * commands run by bash have signal handlers
3366 * set to the values inherited by the shell
3367 * from its parent". */
3368 new_act = S_IGN;
3369 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003370 case SIGTERM:
3371 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003372 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373 break;
3374#if JOBS
3375 case SIGTSTP:
3376 case SIGTTOU:
3377 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003378 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003379 break;
3380#endif
3381 }
3382 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003383//TODO: if !rootshell, we reset SIGQUIT to DFL,
3384//whereas we have to restore it to what shell got on entry
3385//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003386
3387 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003388 cur_act = *t;
3389 if (cur_act == 0) {
3390 /* current setting is not yet known */
3391 if (sigaction(signo, NULL, &act)) {
3392 /* pretend it worked; maybe we should give a warning,
3393 * but other shells don't. We don't alter sigmode,
3394 * so we retry every time.
3395 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003396 return;
3397 }
3398 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003399 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003400 if (mflag
3401 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3402 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003403 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003404 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003405 }
3406 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003407 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003408 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003409
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003410 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003411 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003412 case S_CATCH:
3413 act.sa_handler = onsig;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003414 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3415 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003416 break;
3417 case S_IGN:
3418 act.sa_handler = SIG_IGN;
3419 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003421 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003422
3423 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003424}
3425
3426/* mode flags for set_curjob */
3427#define CUR_DELETE 2
3428#define CUR_RUNNING 1
3429#define CUR_STOPPED 0
3430
3431/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003432#define DOWAIT_NONBLOCK WNOHANG
3433#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003434
3435#if JOBS
3436/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003437static int initialpgrp; //references:2
3438static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003439#endif
3440/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003441static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003442/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003443static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003444/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003445static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003446/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003447static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003448
3449static void
3450set_curjob(struct job *jp, unsigned mode)
3451{
3452 struct job *jp1;
3453 struct job **jpp, **curp;
3454
3455 /* first remove from list */
3456 jpp = curp = &curjob;
3457 do {
3458 jp1 = *jpp;
3459 if (jp1 == jp)
3460 break;
3461 jpp = &jp1->prev_job;
3462 } while (1);
3463 *jpp = jp1->prev_job;
3464
3465 /* Then re-insert in correct position */
3466 jpp = curp;
3467 switch (mode) {
3468 default:
3469#if DEBUG
3470 abort();
3471#endif
3472 case CUR_DELETE:
3473 /* job being deleted */
3474 break;
3475 case CUR_RUNNING:
3476 /* newly created job or backgrounded job,
3477 put after all stopped jobs. */
3478 do {
3479 jp1 = *jpp;
3480#if JOBS
3481 if (!jp1 || jp1->state != JOBSTOPPED)
3482#endif
3483 break;
3484 jpp = &jp1->prev_job;
3485 } while (1);
3486 /* FALLTHROUGH */
3487#if JOBS
3488 case CUR_STOPPED:
3489#endif
3490 /* newly stopped job - becomes curjob */
3491 jp->prev_job = *jpp;
3492 *jpp = jp;
3493 break;
3494 }
3495}
3496
3497#if JOBS || DEBUG
3498static int
3499jobno(const struct job *jp)
3500{
3501 return jp - jobtab + 1;
3502}
3503#endif
3504
3505/*
3506 * Convert a job name to a job structure.
3507 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003508#if !JOBS
3509#define getjob(name, getctl) getjob(name)
3510#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003511static struct job *
3512getjob(const char *name, int getctl)
3513{
3514 struct job *jp;
3515 struct job *found;
3516 const char *err_msg = "No such job: %s";
3517 unsigned num;
3518 int c;
3519 const char *p;
3520 char *(*match)(const char *, const char *);
3521
3522 jp = curjob;
3523 p = name;
3524 if (!p)
3525 goto currentjob;
3526
3527 if (*p != '%')
3528 goto err;
3529
3530 c = *++p;
3531 if (!c)
3532 goto currentjob;
3533
3534 if (!p[1]) {
3535 if (c == '+' || c == '%') {
3536 currentjob:
3537 err_msg = "No current job";
3538 goto check;
3539 }
3540 if (c == '-') {
3541 if (jp)
3542 jp = jp->prev_job;
3543 err_msg = "No previous job";
3544 check:
3545 if (!jp)
3546 goto err;
3547 goto gotit;
3548 }
3549 }
3550
3551 if (is_number(p)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00003552// TODO: number() instead? It does error checking...
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003553 num = atoi(p);
3554 if (num < njobs) {
3555 jp = jobtab + num - 1;
3556 if (jp->used)
3557 goto gotit;
3558 goto err;
3559 }
3560 }
3561
3562 match = prefix;
3563 if (*p == '?') {
3564 match = strstr;
3565 p++;
3566 }
3567
3568 found = 0;
3569 while (1) {
3570 if (!jp)
3571 goto err;
3572 if (match(jp->ps[0].cmd, p)) {
3573 if (found)
3574 goto err;
3575 found = jp;
3576 err_msg = "%s: ambiguous";
3577 }
3578 jp = jp->prev_job;
3579 }
3580
3581 gotit:
3582#if JOBS
3583 err_msg = "job %s not created under job control";
3584 if (getctl && jp->jobctl == 0)
3585 goto err;
3586#endif
3587 return jp;
3588 err:
3589 ash_msg_and_raise_error(err_msg, name);
3590}
3591
3592/*
3593 * Mark a job structure as unused.
3594 */
3595static void
3596freejob(struct job *jp)
3597{
3598 struct procstat *ps;
3599 int i;
3600
3601 INT_OFF;
3602 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
3603 if (ps->cmd != nullstr)
3604 free(ps->cmd);
3605 }
3606 if (jp->ps != &jp->ps0)
3607 free(jp->ps);
3608 jp->used = 0;
3609 set_curjob(jp, CUR_DELETE);
3610 INT_ON;
3611}
3612
3613#if JOBS
3614static void
3615xtcsetpgrp(int fd, pid_t pgrp)
3616{
3617 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003618 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003619}
3620
3621/*
3622 * Turn job control on and off.
3623 *
3624 * Note: This code assumes that the third arg to ioctl is a character
3625 * pointer, which is true on Berkeley systems but not System V. Since
3626 * System V doesn't have job control yet, this isn't a problem now.
3627 *
3628 * Called with interrupts off.
3629 */
3630static void
3631setjobctl(int on)
3632{
3633 int fd;
3634 int pgrp;
3635
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003636 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637 return;
3638 if (on) {
3639 int ofd;
3640 ofd = fd = open(_PATH_TTY, O_RDWR);
3641 if (fd < 0) {
3642 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3643 * That sometimes helps to acquire controlling tty.
3644 * Obviously, a workaround for bugs when someone
3645 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003646 fd = 2;
3647 while (!isatty(fd))
3648 if (--fd < 0)
3649 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003650 }
3651 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003652 if (ofd >= 0)
3653 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654 if (fd < 0)
3655 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003656 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003657 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003658 do { /* while we are in the background */
3659 pgrp = tcgetpgrp(fd);
3660 if (pgrp < 0) {
3661 out:
3662 ash_msg("can't access tty; job control turned off");
3663 mflag = on = 0;
3664 goto close;
3665 }
3666 if (pgrp == getpgrp())
3667 break;
3668 killpg(0, SIGTTIN);
3669 } while (1);
3670 initialpgrp = pgrp;
3671
3672 setsignal(SIGTSTP);
3673 setsignal(SIGTTOU);
3674 setsignal(SIGTTIN);
3675 pgrp = rootpid;
3676 setpgid(0, pgrp);
3677 xtcsetpgrp(fd, pgrp);
3678 } else {
3679 /* turning job control off */
3680 fd = ttyfd;
3681 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003682 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003683 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003684 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003685 setpgid(0, pgrp);
3686 setsignal(SIGTSTP);
3687 setsignal(SIGTTOU);
3688 setsignal(SIGTTIN);
3689 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003690 if (fd >= 0)
3691 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003692 fd = -1;
3693 }
3694 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003695 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003696}
3697
3698static int
3699killcmd(int argc, char **argv)
3700{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003701 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003702 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003703 do {
3704 if (argv[i][0] == '%') {
3705 struct job *jp = getjob(argv[i], 0);
3706 unsigned pid = jp->ps[0].pid;
3707 /* Enough space for ' -NNN<nul>' */
3708 argv[i] = alloca(sizeof(int)*3 + 3);
3709 /* kill_main has matching code to expect
3710 * leading space. Needed to not confuse
3711 * negative pids with "kill -SIGNAL_NO" syntax */
3712 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003713 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003714 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003715 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003716 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717}
3718
3719static void
3720showpipe(struct job *jp, FILE *out)
3721{
3722 struct procstat *sp;
3723 struct procstat *spend;
3724
3725 spend = jp->ps + jp->nprocs;
3726 for (sp = jp->ps + 1; sp < spend; sp++)
3727 fprintf(out, " | %s", sp->cmd);
3728 outcslow('\n', out);
3729 flush_stdout_stderr();
3730}
3731
3732
3733static int
3734restartjob(struct job *jp, int mode)
3735{
3736 struct procstat *ps;
3737 int i;
3738 int status;
3739 pid_t pgid;
3740
3741 INT_OFF;
3742 if (jp->state == JOBDONE)
3743 goto out;
3744 jp->state = JOBRUNNING;
3745 pgid = jp->ps->pid;
3746 if (mode == FORK_FG)
3747 xtcsetpgrp(ttyfd, pgid);
3748 killpg(pgid, SIGCONT);
3749 ps = jp->ps;
3750 i = jp->nprocs;
3751 do {
3752 if (WIFSTOPPED(ps->status)) {
3753 ps->status = -1;
3754 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003755 ps++;
3756 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003757 out:
3758 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3759 INT_ON;
3760 return status;
3761}
3762
3763static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003764fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765{
3766 struct job *jp;
3767 FILE *out;
3768 int mode;
3769 int retval;
3770
3771 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3772 nextopt(nullstr);
3773 argv = argptr;
3774 out = stdout;
3775 do {
3776 jp = getjob(*argv, 1);
3777 if (mode == FORK_BG) {
3778 set_curjob(jp, CUR_RUNNING);
3779 fprintf(out, "[%d] ", jobno(jp));
3780 }
3781 outstr(jp->ps->cmd, out);
3782 showpipe(jp, out);
3783 retval = restartjob(jp, mode);
3784 } while (*argv && *++argv);
3785 return retval;
3786}
3787#endif
3788
3789static int
3790sprint_status(char *s, int status, int sigonly)
3791{
3792 int col;
3793 int st;
3794
3795 col = 0;
3796 if (!WIFEXITED(status)) {
3797#if JOBS
3798 if (WIFSTOPPED(status))
3799 st = WSTOPSIG(status);
3800 else
3801#endif
3802 st = WTERMSIG(status);
3803 if (sigonly) {
3804 if (st == SIGINT || st == SIGPIPE)
3805 goto out;
3806#if JOBS
3807 if (WIFSTOPPED(status))
3808 goto out;
3809#endif
3810 }
3811 st &= 0x7f;
3812 col = fmtstr(s, 32, strsignal(st));
3813 if (WCOREDUMP(status)) {
3814 col += fmtstr(s + col, 16, " (core dumped)");
3815 }
3816 } else if (!sigonly) {
3817 st = WEXITSTATUS(status);
3818 if (st)
3819 col = fmtstr(s, 16, "Done(%d)", st);
3820 else
3821 col = fmtstr(s, 16, "Done");
3822 }
3823 out:
3824 return col;
3825}
3826
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003828dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003829{
3830 int pid;
3831 int status;
3832 struct job *jp;
3833 struct job *thisjob;
3834 int state;
3835
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003836 TRACE(("dowait(0x%x) called\n", wait_flags));
3837
3838 /* Do a wait system call. If job control is compiled in, we accept
3839 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3840 * NB: _not_ safe_waitpid, we need to detect EINTR */
3841 pid = waitpid(-1, &status,
3842 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003843 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3844 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003845 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003846 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003847
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003848 INT_OFF;
3849 thisjob = NULL;
3850 for (jp = curjob; jp; jp = jp->prev_job) {
3851 struct procstat *sp;
3852 struct procstat *spend;
3853 if (jp->state == JOBDONE)
3854 continue;
3855 state = JOBDONE;
3856 spend = jp->ps + jp->nprocs;
3857 sp = jp->ps;
3858 do {
3859 if (sp->pid == pid) {
3860 TRACE(("Job %d: changing status of proc %d "
3861 "from 0x%x to 0x%x\n",
3862 jobno(jp), pid, sp->status, status));
3863 sp->status = status;
3864 thisjob = jp;
3865 }
3866 if (sp->status == -1)
3867 state = JOBRUNNING;
3868#if JOBS
3869 if (state == JOBRUNNING)
3870 continue;
3871 if (WIFSTOPPED(sp->status)) {
3872 jp->stopstatus = sp->status;
3873 state = JOBSTOPPED;
3874 }
3875#endif
3876 } while (++sp < spend);
3877 if (thisjob)
3878 goto gotjob;
3879 }
3880#if JOBS
3881 if (!WIFSTOPPED(status))
3882#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883 jobless--;
3884 goto out;
3885
3886 gotjob:
3887 if (state != JOBRUNNING) {
3888 thisjob->changed = 1;
3889
3890 if (thisjob->state != state) {
3891 TRACE(("Job %d: changing state from %d to %d\n",
3892 jobno(thisjob), thisjob->state, state));
3893 thisjob->state = state;
3894#if JOBS
3895 if (state == JOBSTOPPED) {
3896 set_curjob(thisjob, CUR_STOPPED);
3897 }
3898#endif
3899 }
3900 }
3901
3902 out:
3903 INT_ON;
3904
3905 if (thisjob && thisjob == job) {
3906 char s[48 + 1];
3907 int len;
3908
3909 len = sprint_status(s, status, 1);
3910 if (len) {
3911 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003912 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003913 out2str(s);
3914 }
3915 }
3916 return pid;
3917}
3918
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003919static int
3920blocking_wait_with_raise_on_sig(struct job *job)
3921{
3922 pid_t pid = dowait(DOWAIT_BLOCK, job);
3923 if (pid <= 0 && pendingsig)
3924 raise_exception(EXSIG);
3925 return pid;
3926}
3927
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003928#if JOBS
3929static void
3930showjob(FILE *out, struct job *jp, int mode)
3931{
3932 struct procstat *ps;
3933 struct procstat *psend;
3934 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003935 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003936 char s[80];
3937
3938 ps = jp->ps;
3939
3940 if (mode & SHOW_PGID) {
3941 /* just output process (group) id of pipeline */
3942 fprintf(out, "%d\n", ps->pid);
3943 return;
3944 }
3945
3946 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003947 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003948
3949 if (jp == curjob)
3950 s[col - 2] = '+';
3951 else if (curjob && jp == curjob->prev_job)
3952 s[col - 2] = '-';
3953
3954 if (mode & SHOW_PID)
3955 col += fmtstr(s + col, 16, "%d ", ps->pid);
3956
3957 psend = ps + jp->nprocs;
3958
3959 if (jp->state == JOBRUNNING) {
3960 strcpy(s + col, "Running");
3961 col += sizeof("Running") - 1;
3962 } else {
3963 int status = psend[-1].status;
3964 if (jp->state == JOBSTOPPED)
3965 status = jp->stopstatus;
3966 col += sprint_status(s + col, status, 0);
3967 }
3968
3969 goto start;
3970
3971 do {
3972 /* for each process */
Denis Vlasenko40ba9982007-07-14 00:48:29 +00003973 col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974 start:
3975 fprintf(out, "%s%*c%s",
3976 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
3977 );
3978 if (!(mode & SHOW_PID)) {
3979 showpipe(jp, out);
3980 break;
3981 }
3982 if (++ps == psend) {
3983 outcslow('\n', out);
3984 break;
3985 }
3986 } while (1);
3987
3988 jp->changed = 0;
3989
3990 if (jp->state == JOBDONE) {
3991 TRACE(("showjob: freeing job %d\n", jobno(jp)));
3992 freejob(jp);
3993 }
3994}
3995
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003996/*
3997 * Print a list of jobs. If "change" is nonzero, only print jobs whose
3998 * statuses have changed since the last call to showjobs.
3999 */
4000static void
4001showjobs(FILE *out, int mode)
4002{
4003 struct job *jp;
4004
4005 TRACE(("showjobs(%x) called\n", mode));
4006
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004007 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004008 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004009 continue;
4010
4011 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004012 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004014 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004015 }
4016}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004017
4018static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004019jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004020{
4021 int mode, m;
4022
4023 mode = 0;
4024 while ((m = nextopt("lp"))) {
4025 if (m == 'l')
4026 mode = SHOW_PID;
4027 else
4028 mode = SHOW_PGID;
4029 }
4030
4031 argv = argptr;
4032 if (*argv) {
4033 do
4034 showjob(stdout, getjob(*argv,0), mode);
4035 while (*++argv);
4036 } else
4037 showjobs(stdout, mode);
4038
4039 return 0;
4040}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041#endif /* JOBS */
4042
4043static int
4044getstatus(struct job *job)
4045{
4046 int status;
4047 int retval;
4048
4049 status = job->ps[job->nprocs - 1].status;
4050 retval = WEXITSTATUS(status);
4051 if (!WIFEXITED(status)) {
4052#if JOBS
4053 retval = WSTOPSIG(status);
4054 if (!WIFSTOPPED(status))
4055#endif
4056 {
4057 /* XXX: limits number of signals */
4058 retval = WTERMSIG(status);
4059#if JOBS
4060 if (retval == SIGINT)
4061 job->sigint = 1;
4062#endif
4063 }
4064 retval += 128;
4065 }
4066 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
4067 jobno(job), job->nprocs, status, retval));
4068 return retval;
4069}
4070
4071static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004072waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004073{
4074 struct job *job;
4075 int retval;
4076 struct job *jp;
4077
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004078// exsig++;
4079// xbarrier();
4080 if (pendingsig)
4081 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082
4083 nextopt(nullstr);
4084 retval = 0;
4085
4086 argv = argptr;
4087 if (!*argv) {
4088 /* wait for all jobs */
4089 for (;;) {
4090 jp = curjob;
4091 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004092 if (!jp) /* no running procs */
4093 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004094 if (jp->state == JOBRUNNING)
4095 break;
4096 jp->waited = 1;
4097 jp = jp->prev_job;
4098 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004099 /* man bash:
4100 * "When bash is waiting for an asynchronous command via
4101 * the wait builtin, the reception of a signal for which a trap
4102 * has been set will cause the wait builtin to return immediately
4103 * with an exit status greater than 128, immediately after which
4104 * the trap is executed."
4105 * Do we do it that way? */
4106 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107 }
4108 }
4109
4110 retval = 127;
4111 do {
4112 if (**argv != '%') {
4113 pid_t pid = number(*argv);
4114 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004115 while (1) {
4116 if (!job)
4117 goto repeat;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118 if (job->ps[job->nprocs - 1].pid == pid)
4119 break;
4120 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004121 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004122 } else
4123 job = getjob(*argv, 0);
4124 /* loop until process terminated or stopped */
4125 while (job->state == JOBRUNNING)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004126 blocking_wait_with_raise_on_sig(NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004127 job->waited = 1;
4128 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004129 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004130 } while (*++argv);
4131
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004132 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004133 return retval;
4134}
4135
4136static struct job *
4137growjobtab(void)
4138{
4139 size_t len;
4140 ptrdiff_t offset;
4141 struct job *jp, *jq;
4142
4143 len = njobs * sizeof(*jp);
4144 jq = jobtab;
4145 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4146
4147 offset = (char *)jp - (char *)jq;
4148 if (offset) {
4149 /* Relocate pointers */
4150 size_t l = len;
4151
4152 jq = (struct job *)((char *)jq + l);
4153 while (l) {
4154 l -= sizeof(*jp);
4155 jq--;
4156#define joff(p) ((struct job *)((char *)(p) + l))
4157#define jmove(p) (p) = (void *)((char *)(p) + offset)
4158 if (joff(jp)->ps == &jq->ps0)
4159 jmove(joff(jp)->ps);
4160 if (joff(jp)->prev_job)
4161 jmove(joff(jp)->prev_job);
4162 }
4163 if (curjob)
4164 jmove(curjob);
4165#undef joff
4166#undef jmove
4167 }
4168
4169 njobs += 4;
4170 jobtab = jp;
4171 jp = (struct job *)((char *)jp + len);
4172 jq = jp + 3;
4173 do {
4174 jq->used = 0;
4175 } while (--jq >= jp);
4176 return jp;
4177}
4178
4179/*
4180 * Return a new job structure.
4181 * Called with interrupts off.
4182 */
4183static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004184makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004185{
4186 int i;
4187 struct job *jp;
4188
4189 for (i = njobs, jp = jobtab; ; jp++) {
4190 if (--i < 0) {
4191 jp = growjobtab();
4192 break;
4193 }
4194 if (jp->used == 0)
4195 break;
4196 if (jp->state != JOBDONE || !jp->waited)
4197 continue;
4198#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004199 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004200 continue;
4201#endif
4202 freejob(jp);
4203 break;
4204 }
4205 memset(jp, 0, sizeof(*jp));
4206#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004207 /* jp->jobctl is a bitfield.
4208 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004209 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004210 jp->jobctl = 1;
4211#endif
4212 jp->prev_job = curjob;
4213 curjob = jp;
4214 jp->used = 1;
4215 jp->ps = &jp->ps0;
4216 if (nprocs > 1) {
4217 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4218 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004219 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004220 jobno(jp)));
4221 return jp;
4222}
4223
4224#if JOBS
4225/*
4226 * Return a string identifying a command (to be printed by the
4227 * jobs command).
4228 */
4229static char *cmdnextc;
4230
4231static void
4232cmdputs(const char *s)
4233{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004234 static const char vstype[VSTYPE + 1][3] = {
4235 "", "}", "-", "+", "?", "=",
4236 "%", "%%", "#", "##"
4237 USE_ASH_BASH_COMPAT(, ":", "/", "//")
4238 };
4239
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004240 const char *p, *str;
4241 char c, cc[2] = " ";
4242 char *nextc;
4243 int subtype = 0;
4244 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004245
4246 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4247 p = s;
4248 while ((c = *p++) != 0) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004249 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004250 switch (c) {
4251 case CTLESC:
4252 c = *p++;
4253 break;
4254 case CTLVAR:
4255 subtype = *p++;
4256 if ((subtype & VSTYPE) == VSLENGTH)
4257 str = "${#";
4258 else
4259 str = "${";
4260 if (!(subtype & VSQUOTE) == !(quoted & 1))
4261 goto dostr;
4262 quoted ^= 1;
4263 c = '"';
4264 break;
4265 case CTLENDVAR:
4266 str = "\"}" + !(quoted & 1);
4267 quoted >>= 1;
4268 subtype = 0;
4269 goto dostr;
4270 case CTLBACKQ:
4271 str = "$(...)";
4272 goto dostr;
4273 case CTLBACKQ+CTLQUOTE:
4274 str = "\"$(...)\"";
4275 goto dostr;
4276#if ENABLE_ASH_MATH_SUPPORT
4277 case CTLARI:
4278 str = "$((";
4279 goto dostr;
4280 case CTLENDARI:
4281 str = "))";
4282 goto dostr;
4283#endif
4284 case CTLQUOTEMARK:
4285 quoted ^= 1;
4286 c = '"';
4287 break;
4288 case '=':
4289 if (subtype == 0)
4290 break;
4291 if ((subtype & VSTYPE) != VSNORMAL)
4292 quoted <<= 1;
4293 str = vstype[subtype & VSTYPE];
4294 if (subtype & VSNUL)
4295 c = ':';
4296 else
4297 goto checkstr;
4298 break;
4299 case '\'':
4300 case '\\':
4301 case '"':
4302 case '$':
4303 /* These can only happen inside quotes */
4304 cc[0] = c;
4305 str = cc;
4306 c = '\\';
4307 break;
4308 default:
4309 break;
4310 }
4311 USTPUTC(c, nextc);
4312 checkstr:
4313 if (!str)
4314 continue;
4315 dostr:
4316 while ((c = *str++)) {
4317 USTPUTC(c, nextc);
4318 }
4319 }
4320 if (quoted & 1) {
4321 USTPUTC('"', nextc);
4322 }
4323 *nextc = 0;
4324 cmdnextc = nextc;
4325}
4326
4327/* cmdtxt() and cmdlist() call each other */
4328static void cmdtxt(union node *n);
4329
4330static void
4331cmdlist(union node *np, int sep)
4332{
4333 for (; np; np = np->narg.next) {
4334 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004335 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004336 cmdtxt(np);
4337 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004338 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004339 }
4340}
4341
4342static void
4343cmdtxt(union node *n)
4344{
4345 union node *np;
4346 struct nodelist *lp;
4347 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004348
4349 if (!n)
4350 return;
4351 switch (n->type) {
4352 default:
4353#if DEBUG
4354 abort();
4355#endif
4356 case NPIPE:
4357 lp = n->npipe.cmdlist;
4358 for (;;) {
4359 cmdtxt(lp->n);
4360 lp = lp->next;
4361 if (!lp)
4362 break;
4363 cmdputs(" | ");
4364 }
4365 break;
4366 case NSEMI:
4367 p = "; ";
4368 goto binop;
4369 case NAND:
4370 p = " && ";
4371 goto binop;
4372 case NOR:
4373 p = " || ";
4374 binop:
4375 cmdtxt(n->nbinary.ch1);
4376 cmdputs(p);
4377 n = n->nbinary.ch2;
4378 goto donode;
4379 case NREDIR:
4380 case NBACKGND:
4381 n = n->nredir.n;
4382 goto donode;
4383 case NNOT:
4384 cmdputs("!");
4385 n = n->nnot.com;
4386 donode:
4387 cmdtxt(n);
4388 break;
4389 case NIF:
4390 cmdputs("if ");
4391 cmdtxt(n->nif.test);
4392 cmdputs("; then ");
4393 n = n->nif.ifpart;
4394 if (n->nif.elsepart) {
4395 cmdtxt(n);
4396 cmdputs("; else ");
4397 n = n->nif.elsepart;
4398 }
4399 p = "; fi";
4400 goto dotail;
4401 case NSUBSHELL:
4402 cmdputs("(");
4403 n = n->nredir.n;
4404 p = ")";
4405 goto dotail;
4406 case NWHILE:
4407 p = "while ";
4408 goto until;
4409 case NUNTIL:
4410 p = "until ";
4411 until:
4412 cmdputs(p);
4413 cmdtxt(n->nbinary.ch1);
4414 n = n->nbinary.ch2;
4415 p = "; done";
4416 dodo:
4417 cmdputs("; do ");
4418 dotail:
4419 cmdtxt(n);
4420 goto dotail2;
4421 case NFOR:
4422 cmdputs("for ");
4423 cmdputs(n->nfor.var);
4424 cmdputs(" in ");
4425 cmdlist(n->nfor.args, 1);
4426 n = n->nfor.body;
4427 p = "; done";
4428 goto dodo;
4429 case NDEFUN:
4430 cmdputs(n->narg.text);
4431 p = "() { ... }";
4432 goto dotail2;
4433 case NCMD:
4434 cmdlist(n->ncmd.args, 1);
4435 cmdlist(n->ncmd.redirect, 0);
4436 break;
4437 case NARG:
4438 p = n->narg.text;
4439 dotail2:
4440 cmdputs(p);
4441 break;
4442 case NHERE:
4443 case NXHERE:
4444 p = "<<...";
4445 goto dotail2;
4446 case NCASE:
4447 cmdputs("case ");
4448 cmdputs(n->ncase.expr->narg.text);
4449 cmdputs(" in ");
4450 for (np = n->ncase.cases; np; np = np->nclist.next) {
4451 cmdtxt(np->nclist.pattern);
4452 cmdputs(") ");
4453 cmdtxt(np->nclist.body);
4454 cmdputs(";; ");
4455 }
4456 p = "esac";
4457 goto dotail2;
4458 case NTO:
4459 p = ">";
4460 goto redir;
4461 case NCLOBBER:
4462 p = ">|";
4463 goto redir;
4464 case NAPPEND:
4465 p = ">>";
4466 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004467#if ENABLE_ASH_BASH_COMPAT
4468 case NTO2:
4469#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004470 case NTOFD:
4471 p = ">&";
4472 goto redir;
4473 case NFROM:
4474 p = "<";
4475 goto redir;
4476 case NFROMFD:
4477 p = "<&";
4478 goto redir;
4479 case NFROMTO:
4480 p = "<>";
4481 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004482 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004483 cmdputs(p);
4484 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004485 cmdputs(utoa(n->ndup.dupfd));
4486 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004487 }
4488 n = n->nfile.fname;
4489 goto donode;
4490 }
4491}
4492
4493static char *
4494commandtext(union node *n)
4495{
4496 char *name;
4497
4498 STARTSTACKSTR(cmdnextc);
4499 cmdtxt(n);
4500 name = stackblock();
4501 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4502 name, cmdnextc, cmdnextc));
4503 return ckstrdup(name);
4504}
4505#endif /* JOBS */
4506
4507/*
4508 * Fork off a subshell. If we are doing job control, give the subshell its
4509 * own process group. Jp is a job structure that the job is to be added to.
4510 * N is the command that will be evaluated by the child. Both jp and n may
4511 * be NULL. The mode parameter can be one of the following:
4512 * FORK_FG - Fork off a foreground process.
4513 * FORK_BG - Fork off a background process.
4514 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4515 * process group even if job control is on.
4516 *
4517 * When job control is turned off, background processes have their standard
4518 * input redirected to /dev/null (except for the second and later processes
4519 * in a pipeline).
4520 *
4521 * Called with interrupts off.
4522 */
4523/*
4524 * Clear traps on a fork.
4525 */
4526static void
4527clear_traps(void)
4528{
4529 char **tp;
4530
4531 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004532 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004533 INT_OFF;
4534 free(*tp);
4535 *tp = NULL;
4536 if (tp != &trap[0])
4537 setsignal(tp - trap);
4538 INT_ON;
4539 }
4540 }
4541}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004542
4543/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004545
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004546/* Called after fork(), in child */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004547static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00004548forkchild(struct job *jp, /*union node *n,*/ int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004549{
4550 int oldlvl;
4551
4552 TRACE(("Child shell %d\n", getpid()));
4553 oldlvl = shlvl;
4554 shlvl++;
4555
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004556 /* man bash: "Non-builtin commands run by bash have signal handlers
4557 * set to the values inherited by the shell from its parent".
4558 * Do we do it correctly? */
4559
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004560 closescript();
4561 clear_traps();
4562#if JOBS
4563 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004564 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004565 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4566 pid_t pgrp;
4567
4568 if (jp->nprocs == 0)
4569 pgrp = getpid();
4570 else
4571 pgrp = jp->ps[0].pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004572 /* this can fail because we are doing it in the parent also */
4573 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004574 if (mode == FORK_FG)
4575 xtcsetpgrp(ttyfd, pgrp);
4576 setsignal(SIGTSTP);
4577 setsignal(SIGTTOU);
4578 } else
4579#endif
4580 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004581 /* man bash: "When job control is not in effect,
4582 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004583 ignoresig(SIGINT);
4584 ignoresig(SIGQUIT);
4585 if (jp->nprocs == 0) {
4586 close(0);
4587 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004588 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004589 }
4590 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004591 if (!oldlvl) {
4592 if (iflag) { /* why if iflag only? */
4593 setsignal(SIGINT);
4594 setsignal(SIGTERM);
4595 }
4596 /* man bash:
4597 * "In all cases, bash ignores SIGQUIT. Non-builtin
4598 * commands run by bash have signal handlers
4599 * set to the values inherited by the shell
4600 * from its parent".
4601 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004602 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004603 }
4604 for (jp = curjob; jp; jp = jp->prev_job)
4605 freejob(jp);
4606 jobless = 0;
4607}
4608
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004609/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004610#if !JOBS
4611#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4612#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004613static void
4614forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4615{
4616 TRACE(("In parent shell: child = %d\n", pid));
4617 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004618 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4619 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004620 jobless++;
4621 return;
4622 }
4623#if JOBS
4624 if (mode != FORK_NOJOB && jp->jobctl) {
4625 int pgrp;
4626
4627 if (jp->nprocs == 0)
4628 pgrp = pid;
4629 else
4630 pgrp = jp->ps[0].pid;
4631 /* This can fail because we are doing it in the child also */
4632 setpgid(pid, pgrp);
4633 }
4634#endif
4635 if (mode == FORK_BG) {
4636 backgndpid = pid; /* set $! */
4637 set_curjob(jp, CUR_RUNNING);
4638 }
4639 if (jp) {
4640 struct procstat *ps = &jp->ps[jp->nprocs++];
4641 ps->pid = pid;
4642 ps->status = -1;
4643 ps->cmd = nullstr;
4644#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004645 if (doing_jobctl && n)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004646 ps->cmd = commandtext(n);
4647#endif
4648 }
4649}
4650
4651static int
4652forkshell(struct job *jp, union node *n, int mode)
4653{
4654 int pid;
4655
4656 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4657 pid = fork();
4658 if (pid < 0) {
4659 TRACE(("Fork failed, errno=%d", errno));
4660 if (jp)
4661 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004662 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004663 }
4664 if (pid == 0)
Denis Vlasenko68404f12008-03-17 09:00:54 +00004665 forkchild(jp, /*n,*/ mode);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004666 else
4667 forkparent(jp, n, mode, pid);
4668 return pid;
4669}
4670
4671/*
4672 * Wait for job to finish.
4673 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004674 * Under job control we have the problem that while a child process
4675 * is running interrupts generated by the user are sent to the child
4676 * but not to the shell. This means that an infinite loop started by
4677 * an interactive user may be hard to kill. With job control turned off,
4678 * an interactive user may place an interactive program inside a loop.
4679 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004680 * these interrupts to also abort the loop. The approach we take here
4681 * is to have the shell ignore interrupt signals while waiting for a
4682 * foreground process to terminate, and then send itself an interrupt
4683 * signal if the child process was terminated by an interrupt signal.
4684 * Unfortunately, some programs want to do a bit of cleanup and then
4685 * exit on interrupt; unless these processes terminate themselves by
4686 * sending a signal to themselves (instead of calling exit) they will
4687 * confuse this approach.
4688 *
4689 * Called with interrupts off.
4690 */
4691static int
4692waitforjob(struct job *jp)
4693{
4694 int st;
4695
4696 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004697
4698 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004699 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004700 /* In non-interactive shells, we _can_ get
4701 * a keyboard signal here and be EINTRed,
4702 * but we just loop back, waiting for command to complete.
4703 *
4704 * man bash:
4705 * "If bash is waiting for a command to complete and receives
4706 * a signal for which a trap has been set, the trap
4707 * will not be executed until the command completes."
4708 *
4709 * Reality is that even if trap is not set, bash
4710 * will not act on the signal until command completes.
4711 * Try this. sleep5intoff.c:
4712 * #include <signal.h>
4713 * #include <unistd.h>
4714 * int main() {
4715 * sigset_t set;
4716 * sigemptyset(&set);
4717 * sigaddset(&set, SIGINT);
4718 * sigaddset(&set, SIGQUIT);
4719 * sigprocmask(SIG_BLOCK, &set, NULL);
4720 * sleep(5);
4721 * return 0;
4722 * }
4723 * $ bash -c './sleep5intoff; echo hi'
4724 * ^C^C^C^C <--- pressing ^C once a second
4725 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004726 * $ bash -c './sleep5intoff; echo hi'
4727 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4728 * $ _
4729 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004730 dowait(DOWAIT_BLOCK, jp);
4731 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004732 INT_ON;
4733
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004734 st = getstatus(jp);
4735#if JOBS
4736 if (jp->jobctl) {
4737 xtcsetpgrp(ttyfd, rootpid);
4738 /*
4739 * This is truly gross.
4740 * If we're doing job control, then we did a TIOCSPGRP which
4741 * caused us (the shell) to no longer be in the controlling
4742 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4743 * intuit from the subprocess exit status whether a SIGINT
4744 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4745 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004746 if (jp->sigint) /* TODO: do the same with all signals */
4747 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004748 }
4749 if (jp->state == JOBDONE)
4750#endif
4751 freejob(jp);
4752 return st;
4753}
4754
4755/*
4756 * return 1 if there are stopped jobs, otherwise 0
4757 */
4758static int
4759stoppedjobs(void)
4760{
4761 struct job *jp;
4762 int retval;
4763
4764 retval = 0;
4765 if (job_warning)
4766 goto out;
4767 jp = curjob;
4768 if (jp && jp->state == JOBSTOPPED) {
4769 out2str("You have stopped jobs.\n");
4770 job_warning = 2;
4771 retval++;
4772 }
4773 out:
4774 return retval;
4775}
4776
4777
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004778/* ============ redir.c
4779 *
4780 * Code for dealing with input/output redirection.
4781 */
4782
4783#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004784#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004785
4786/*
4787 * Open a file in noclobber mode.
4788 * The code was copied from bash.
4789 */
4790static int
4791noclobberopen(const char *fname)
4792{
4793 int r, fd;
4794 struct stat finfo, finfo2;
4795
4796 /*
4797 * If the file exists and is a regular file, return an error
4798 * immediately.
4799 */
4800 r = stat(fname, &finfo);
4801 if (r == 0 && S_ISREG(finfo.st_mode)) {
4802 errno = EEXIST;
4803 return -1;
4804 }
4805
4806 /*
4807 * If the file was not present (r != 0), make sure we open it
4808 * exclusively so that if it is created before we open it, our open
4809 * will fail. Make sure that we do not truncate an existing file.
4810 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4811 * file was not a regular file, we leave O_EXCL off.
4812 */
4813 if (r != 0)
4814 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4815 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4816
4817 /* If the open failed, return the file descriptor right away. */
4818 if (fd < 0)
4819 return fd;
4820
4821 /*
4822 * OK, the open succeeded, but the file may have been changed from a
4823 * non-regular file to a regular file between the stat and the open.
4824 * We are assuming that the O_EXCL open handles the case where FILENAME
4825 * did not exist and is symlinked to an existing file between the stat
4826 * and open.
4827 */
4828
4829 /*
4830 * If we can open it and fstat the file descriptor, and neither check
4831 * revealed that it was a regular file, and the file has not been
4832 * replaced, return the file descriptor.
4833 */
4834 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
4835 && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
4836 return fd;
4837
4838 /* The file has been replaced. badness. */
4839 close(fd);
4840 errno = EEXIST;
4841 return -1;
4842}
4843
4844/*
4845 * Handle here documents. Normally we fork off a process to write the
4846 * data to a pipe. If the document is short, we can stuff the data in
4847 * the pipe without forking.
4848 */
4849/* openhere needs this forward reference */
4850static void expandhere(union node *arg, int fd);
4851static int
4852openhere(union node *redir)
4853{
4854 int pip[2];
4855 size_t len = 0;
4856
4857 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00004858 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004859 if (redir->type == NHERE) {
4860 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004861 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004862 full_write(pip[1], redir->nhere.doc->narg.text, len);
4863 goto out;
4864 }
4865 }
4866 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00004867 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004868 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004869 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
4870 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
4871 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
4872 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004873 signal(SIGPIPE, SIG_DFL);
4874 if (redir->type == NHERE)
4875 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00004876 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004877 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00004878 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004879 }
4880 out:
4881 close(pip[1]);
4882 return pip[0];
4883}
4884
4885static int
4886openredirect(union node *redir)
4887{
4888 char *fname;
4889 int f;
4890
4891 switch (redir->nfile.type) {
4892 case NFROM:
4893 fname = redir->nfile.expfname;
4894 f = open(fname, O_RDONLY);
4895 if (f < 0)
4896 goto eopen;
4897 break;
4898 case NFROMTO:
4899 fname = redir->nfile.expfname;
4900 f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
4901 if (f < 0)
4902 goto ecreate;
4903 break;
4904 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00004905#if ENABLE_ASH_BASH_COMPAT
4906 case NTO2:
4907#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004908 /* Take care of noclobber mode. */
4909 if (Cflag) {
4910 fname = redir->nfile.expfname;
4911 f = noclobberopen(fname);
4912 if (f < 0)
4913 goto ecreate;
4914 break;
4915 }
4916 /* FALLTHROUGH */
4917 case NCLOBBER:
4918 fname = redir->nfile.expfname;
4919 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
4920 if (f < 0)
4921 goto ecreate;
4922 break;
4923 case NAPPEND:
4924 fname = redir->nfile.expfname;
4925 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
4926 if (f < 0)
4927 goto ecreate;
4928 break;
4929 default:
4930#if DEBUG
4931 abort();
4932#endif
4933 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004934/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00004935// case NTOFD:
4936// case NFROMFD:
4937// f = -1;
4938// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004939 case NHERE:
4940 case NXHERE:
4941 f = openhere(redir);
4942 break;
4943 }
4944
4945 return f;
4946 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004947 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004948 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00004949 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004950}
4951
4952/*
4953 * Copy a file descriptor to be >= to. Returns -1
4954 * if the source file descriptor is closed, EMPTY if there are no unused
4955 * file descriptors left.
4956 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00004957/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
4958 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00004959enum {
4960 COPYFD_EXACT = (int)~(INT_MAX),
4961 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
4962};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004963static int
4964copyfd(int from, int to)
4965{
4966 int newfd;
4967
Denis Vlasenko5a867312008-07-24 19:46:38 +00004968 if (to & COPYFD_EXACT) {
4969 to &= ~COPYFD_EXACT;
4970 /*if (from != to)*/
4971 newfd = dup2(from, to);
4972 } else {
4973 newfd = fcntl(from, F_DUPFD, to);
4974 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004975 if (newfd < 0) {
4976 if (errno == EMFILE)
4977 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004978 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004979 ash_msg_and_raise_error("%d: %m", from);
4980 }
4981 return newfd;
4982}
4983
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004984/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004985struct two_fd_t {
4986 int orig, copy;
4987};
Denis Vlasenko0b769642008-07-24 07:54:57 +00004988struct redirtab {
4989 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00004990 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004991 int pair_count;
4992 struct two_fd_t two_fd[0];
Denis Vlasenko0b769642008-07-24 07:54:57 +00004993};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00004994#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00004995
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004996static int need_to_remember(struct redirtab *rp, int fd)
4997{
4998 int i;
4999
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005000 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005001 return 0;
5002
5003 for (i = 0; i < rp->pair_count; i++) {
5004 if (rp->two_fd[i].orig == fd) {
5005 /* already remembered */
5006 return 0;
5007 }
5008 }
5009 return 1;
5010}
5011
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005012/* "hidden" fd is a fd used to read scripts, or a copy of such */
5013static int is_hidden_fd(struct redirtab *rp, int fd)
5014{
5015 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005016 struct parsefile *pf;
5017
5018 if (fd == -1)
5019 return 0;
5020 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005021 while (pf) {
5022 if (fd == pf->fd) {
5023 return 1;
5024 }
5025 pf = pf->prev;
5026 }
5027 if (!rp)
5028 return 0;
5029 fd |= COPYFD_RESTORE;
5030 for (i = 0; i < rp->pair_count; i++) {
5031 if (rp->two_fd[i].copy == fd) {
5032 return 1;
5033 }
5034 }
5035 return 0;
5036}
5037
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005038/*
5039 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5040 * old file descriptors are stashed away so that the redirection can be
5041 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
5042 * standard output, and the standard error if it becomes a duplicate of
5043 * stdout, is saved in memory.
5044 */
5045/* flags passed to redirect */
5046#define REDIR_PUSH 01 /* save previous values of file descriptors */
5047#define REDIR_SAVEFD2 03 /* set preverrout */
5048static void
5049redirect(union node *redir, int flags)
5050{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005051 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005052 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005053 int i;
5054 int fd;
5055 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005056 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005057
Denis Vlasenko01631112007-12-16 17:20:38 +00005058 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005059 if (!redir) {
5060 return;
5061 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005062
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005063 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005064 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005065 INT_OFF;
5066 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005067 union node *tmp = redir;
5068 do {
5069 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005070#if ENABLE_ASH_BASH_COMPAT
5071 if (redir->nfile.type == NTO2)
5072 sv_pos++;
5073#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005074 tmp = tmp->nfile.next;
5075 } while (tmp);
5076 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005077 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005078 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005079 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005080 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005081 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005082 while (sv_pos > 0) {
5083 sv_pos--;
5084 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5085 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005086 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005087
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005088 do {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005089 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005090 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005091 int right_fd = redir->ndup.dupfd;
5092 /* redirect from/to same file descriptor? */
5093 if (right_fd == fd)
5094 continue;
5095 /* echo >&10 and 10 is a fd opened to the sh script? */
5096 if (is_hidden_fd(sv, right_fd)) {
5097 errno = EBADF; /* as if it is closed */
5098 ash_msg_and_raise_error("%d: %m", right_fd);
5099 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005100 newfd = -1;
5101 } else {
5102 newfd = openredirect(redir); /* always >= 0 */
5103 if (fd == newfd) {
5104 /* Descriptor wasn't open before redirect.
5105 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005106 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005107 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005108 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005109 continue;
5110 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005111 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005112#if ENABLE_ASH_BASH_COMPAT
5113 redirect_more:
5114#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005115 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005116 /* Copy old descriptor */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005117 i = fcntl(fd, F_DUPFD, 10);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005118/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5119 * are closed in popredir() in the child, preventing them from leaking
5120 * into child. (popredir() also cleans up the mess in case of failures)
5121 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122 if (i == -1) {
5123 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005124 if (i != EBADF) {
5125 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005126 if (newfd >= 0)
5127 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005128 errno = i;
5129 ash_msg_and_raise_error("%d: %m", fd);
5130 /* NOTREACHED */
5131 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005132 /* EBADF: it is not open - good, remember to close it */
5133 remember_to_close:
5134 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005135 } else { /* fd is open, save its copy */
5136 /* "exec fd>&-" should not close fds
5137 * which point to script file(s).
5138 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005139 if (is_hidden_fd(sv, fd))
5140 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005141 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005142 if (fd == 2)
5143 copied_fd2 = i;
5144 sv->two_fd[sv_pos].orig = fd;
5145 sv->two_fd[sv_pos].copy = i;
5146 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005147 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005148 if (newfd < 0) {
5149 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005150 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005151 /* Don't want to trigger debugging */
5152 if (fd != -1)
5153 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005154 } else {
5155 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005156 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005157 } else if (fd != newfd) { /* move newfd to fd */
5158 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005159#if ENABLE_ASH_BASH_COMPAT
5160 if (!(redir->nfile.type == NTO2 && fd == 2))
5161#endif
5162 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005163 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005164#if ENABLE_ASH_BASH_COMPAT
5165 if (redir->nfile.type == NTO2 && fd == 1) {
5166 /* We already redirected it to fd 1, now copy it to 2 */
5167 newfd = 1;
5168 fd = 2;
5169 goto redirect_more;
5170 }
5171#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005172 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005173
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005174 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005175 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5176 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005177}
5178
5179/*
5180 * Undo the effects of the last redirection.
5181 */
5182static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005183popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005184{
5185 struct redirtab *rp;
5186 int i;
5187
Denis Vlasenko01631112007-12-16 17:20:38 +00005188 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005189 return;
5190 INT_OFF;
5191 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005192 for (i = 0; i < rp->pair_count; i++) {
5193 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005194 int copy = rp->two_fd[i].copy;
5195 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005196 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005197 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005198 continue;
5199 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005200 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005201 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005202 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005203 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005204 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005205 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005206 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005207 }
5208 }
5209 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005210 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005211 free(rp);
5212 INT_ON;
5213}
5214
5215/*
5216 * Undo all redirections. Called on error or interrupt.
5217 */
5218
5219/*
5220 * Discard all saved file descriptors.
5221 */
5222static void
5223clearredir(int drop)
5224{
5225 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005226 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005227 if (!redirlist)
5228 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005229 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005230 }
5231}
5232
5233static int
5234redirectsafe(union node *redir, int flags)
5235{
5236 int err;
5237 volatile int saveint;
5238 struct jmploc *volatile savehandler = exception_handler;
5239 struct jmploc jmploc;
5240
5241 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005242 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5243 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005244 if (!err) {
5245 exception_handler = &jmploc;
5246 redirect(redir, flags);
5247 }
5248 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005249 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005250 longjmp(exception_handler->loc, 1);
5251 RESTORE_INT(saveint);
5252 return err;
5253}
5254
5255
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005256/* ============ Routines to expand arguments to commands
5257 *
5258 * We have to deal with backquotes, shell variables, and file metacharacters.
5259 */
5260
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005261#if ENABLE_ASH_MATH_SUPPORT_64
5262typedef int64_t arith_t;
5263#define arith_t_type long long
5264#else
5265typedef long arith_t;
5266#define arith_t_type long
5267#endif
5268
5269#if ENABLE_ASH_MATH_SUPPORT
5270static arith_t dash_arith(const char *);
5271static arith_t arith(const char *expr, int *perrcode);
5272#endif
5273
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005274/*
5275 * expandarg flags
5276 */
5277#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5278#define EXP_TILDE 0x2 /* do normal tilde expansion */
5279#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5280#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5281#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5282#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5283#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5284#define EXP_WORD 0x80 /* expand word in parameter expansion */
5285#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5286/*
5287 * _rmescape() flags
5288 */
5289#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5290#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5291#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5292#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5293#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5294
5295/*
5296 * Structure specifying which parts of the string should be searched
5297 * for IFS characters.
5298 */
5299struct ifsregion {
5300 struct ifsregion *next; /* next region in list */
5301 int begoff; /* offset of start of region */
5302 int endoff; /* offset of end of region */
5303 int nulonly; /* search for nul bytes only */
5304};
5305
5306struct arglist {
5307 struct strlist *list;
5308 struct strlist **lastp;
5309};
5310
5311/* output of current string */
5312static char *expdest;
5313/* list of back quote expressions */
5314static struct nodelist *argbackq;
5315/* first struct in list of ifs regions */
5316static struct ifsregion ifsfirst;
5317/* last struct in list */
5318static struct ifsregion *ifslastp;
5319/* holds expanded arg list */
5320static struct arglist exparg;
5321
5322/*
5323 * Our own itoa().
5324 */
5325static int
5326cvtnum(arith_t num)
5327{
5328 int len;
5329
5330 expdest = makestrspace(32, expdest);
5331#if ENABLE_ASH_MATH_SUPPORT_64
5332 len = fmtstr(expdest, 32, "%lld", (long long) num);
5333#else
5334 len = fmtstr(expdest, 32, "%ld", num);
5335#endif
5336 STADJUST(len, expdest);
5337 return len;
5338}
5339
5340static size_t
5341esclen(const char *start, const char *p)
5342{
5343 size_t esc = 0;
5344
5345 while (p > start && *--p == CTLESC) {
5346 esc++;
5347 }
5348 return esc;
5349}
5350
5351/*
5352 * Remove any CTLESC characters from a string.
5353 */
5354static char *
5355_rmescapes(char *str, int flag)
5356{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005357 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005358
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005359 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005360 unsigned inquotes;
5361 int notescaped;
5362 int globbing;
5363
5364 p = strpbrk(str, qchars);
5365 if (!p) {
5366 return str;
5367 }
5368 q = p;
5369 r = str;
5370 if (flag & RMESCAPE_ALLOC) {
5371 size_t len = p - str;
5372 size_t fulllen = len + strlen(p) + 1;
5373
5374 if (flag & RMESCAPE_GROW) {
5375 r = makestrspace(fulllen, expdest);
5376 } else if (flag & RMESCAPE_HEAP) {
5377 r = ckmalloc(fulllen);
5378 } else {
5379 r = stalloc(fulllen);
5380 }
5381 q = r;
5382 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005383 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005384 }
5385 }
5386 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5387 globbing = flag & RMESCAPE_GLOB;
5388 notescaped = globbing;
5389 while (*p) {
5390 if (*p == CTLQUOTEMARK) {
5391 inquotes = ~inquotes;
5392 p++;
5393 notescaped = globbing;
5394 continue;
5395 }
5396 if (*p == '\\') {
5397 /* naked back slash */
5398 notescaped = 0;
5399 goto copy;
5400 }
5401 if (*p == CTLESC) {
5402 p++;
5403 if (notescaped && inquotes && *p != '/') {
5404 *q++ = '\\';
5405 }
5406 }
5407 notescaped = globbing;
5408 copy:
5409 *q++ = *p++;
5410 }
5411 *q = '\0';
5412 if (flag & RMESCAPE_GROW) {
5413 expdest = r;
5414 STADJUST(q - r + 1, expdest);
5415 }
5416 return r;
5417}
5418#define rmescapes(p) _rmescapes((p), 0)
5419
5420#define pmatch(a, b) !fnmatch((a), (b), 0)
5421
5422/*
5423 * Prepare a pattern for a expmeta (internal glob(3)) call.
5424 *
5425 * Returns an stalloced string.
5426 */
5427static char *
5428preglob(const char *pattern, int quoted, int flag)
5429{
5430 flag |= RMESCAPE_GLOB;
5431 if (quoted) {
5432 flag |= RMESCAPE_QUOTED;
5433 }
5434 return _rmescapes((char *)pattern, flag);
5435}
5436
5437/*
5438 * Put a string on the stack.
5439 */
5440static void
5441memtodest(const char *p, size_t len, int syntax, int quotes)
5442{
5443 char *q = expdest;
5444
5445 q = makestrspace(len * 2, q);
5446
5447 while (len--) {
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00005448 int c = signed_char2int(*p++);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005449 if (!c)
5450 continue;
5451 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5452 USTPUTC(CTLESC, q);
5453 USTPUTC(c, q);
5454 }
5455
5456 expdest = q;
5457}
5458
5459static void
5460strtodest(const char *p, int syntax, int quotes)
5461{
5462 memtodest(p, strlen(p), syntax, quotes);
5463}
5464
5465/*
5466 * Record the fact that we have to scan this region of the
5467 * string for IFS characters.
5468 */
5469static void
5470recordregion(int start, int end, int nulonly)
5471{
5472 struct ifsregion *ifsp;
5473
5474 if (ifslastp == NULL) {
5475 ifsp = &ifsfirst;
5476 } else {
5477 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005478 ifsp = ckzalloc(sizeof(*ifsp));
5479 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005480 ifslastp->next = ifsp;
5481 INT_ON;
5482 }
5483 ifslastp = ifsp;
5484 ifslastp->begoff = start;
5485 ifslastp->endoff = end;
5486 ifslastp->nulonly = nulonly;
5487}
5488
5489static void
5490removerecordregions(int endoff)
5491{
5492 if (ifslastp == NULL)
5493 return;
5494
5495 if (ifsfirst.endoff > endoff) {
5496 while (ifsfirst.next != NULL) {
5497 struct ifsregion *ifsp;
5498 INT_OFF;
5499 ifsp = ifsfirst.next->next;
5500 free(ifsfirst.next);
5501 ifsfirst.next = ifsp;
5502 INT_ON;
5503 }
5504 if (ifsfirst.begoff > endoff)
5505 ifslastp = NULL;
5506 else {
5507 ifslastp = &ifsfirst;
5508 ifsfirst.endoff = endoff;
5509 }
5510 return;
5511 }
5512
5513 ifslastp = &ifsfirst;
5514 while (ifslastp->next && ifslastp->next->begoff < endoff)
5515 ifslastp=ifslastp->next;
5516 while (ifslastp->next != NULL) {
5517 struct ifsregion *ifsp;
5518 INT_OFF;
5519 ifsp = ifslastp->next->next;
5520 free(ifslastp->next);
5521 ifslastp->next = ifsp;
5522 INT_ON;
5523 }
5524 if (ifslastp->endoff > endoff)
5525 ifslastp->endoff = endoff;
5526}
5527
5528static char *
5529exptilde(char *startp, char *p, int flag)
5530{
5531 char c;
5532 char *name;
5533 struct passwd *pw;
5534 const char *home;
5535 int quotes = flag & (EXP_FULL | EXP_CASE);
5536 int startloc;
5537
5538 name = p + 1;
5539
5540 while ((c = *++p) != '\0') {
5541 switch (c) {
5542 case CTLESC:
5543 return startp;
5544 case CTLQUOTEMARK:
5545 return startp;
5546 case ':':
5547 if (flag & EXP_VARTILDE)
5548 goto done;
5549 break;
5550 case '/':
5551 case CTLENDVAR:
5552 goto done;
5553 }
5554 }
5555 done:
5556 *p = '\0';
5557 if (*name == '\0') {
5558 home = lookupvar(homestr);
5559 } else {
5560 pw = getpwnam(name);
5561 if (pw == NULL)
5562 goto lose;
5563 home = pw->pw_dir;
5564 }
5565 if (!home || !*home)
5566 goto lose;
5567 *p = c;
5568 startloc = expdest - (char *)stackblock();
5569 strtodest(home, SQSYNTAX, quotes);
5570 recordregion(startloc, expdest - (char *)stackblock(), 0);
5571 return p;
5572 lose:
5573 *p = c;
5574 return startp;
5575}
5576
5577/*
5578 * Execute a command inside back quotes. If it's a builtin command, we
5579 * want to save its output in a block obtained from malloc. Otherwise
5580 * we fork off a subprocess and get the output of the command via a pipe.
5581 * Should be called with interrupts off.
5582 */
5583struct backcmd { /* result of evalbackcmd */
5584 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005585 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005586 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587 struct job *jp; /* job structure for command */
5588};
5589
5590/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005591static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005592#define EV_EXIT 01 /* exit after evaluating tree */
5593static void evaltree(union node *, int);
5594
5595static void
5596evalbackcmd(union node *n, struct backcmd *result)
5597{
5598 int saveherefd;
5599
5600 result->fd = -1;
5601 result->buf = NULL;
5602 result->nleft = 0;
5603 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005604 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005605 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606
5607 saveherefd = herefd;
5608 herefd = -1;
5609
5610 {
5611 int pip[2];
5612 struct job *jp;
5613
5614 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005615 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005616 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005617 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5618 FORCE_INT_ON;
5619 close(pip[0]);
5620 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005621 /*close(1);*/
5622 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005623 close(pip[1]);
5624 }
5625 eflag = 0;
5626 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5627 /* NOTREACHED */
5628 }
5629 close(pip[1]);
5630 result->fd = pip[0];
5631 result->jp = jp;
5632 }
5633 herefd = saveherefd;
5634 out:
5635 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5636 result->fd, result->buf, result->nleft, result->jp));
5637}
5638
5639/*
5640 * Expand stuff in backwards quotes.
5641 */
5642static void
5643expbackq(union node *cmd, int quoted, int quotes)
5644{
5645 struct backcmd in;
5646 int i;
5647 char buf[128];
5648 char *p;
5649 char *dest;
5650 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005651 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005652 struct stackmark smark;
5653
5654 INT_OFF;
5655 setstackmark(&smark);
5656 dest = expdest;
5657 startloc = dest - (char *)stackblock();
5658 grabstackstr(dest);
5659 evalbackcmd(cmd, &in);
5660 popstackmark(&smark);
5661
5662 p = in.buf;
5663 i = in.nleft;
5664 if (i == 0)
5665 goto read;
5666 for (;;) {
5667 memtodest(p, i, syntax, quotes);
5668 read:
5669 if (in.fd < 0)
5670 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005671 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005672 TRACE(("expbackq: read returns %d\n", i));
5673 if (i <= 0)
5674 break;
5675 p = buf;
5676 }
5677
Denis Vlasenko60818682007-09-28 22:07:23 +00005678 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005679 if (in.fd >= 0) {
5680 close(in.fd);
5681 back_exitstatus = waitforjob(in.jp);
5682 }
5683 INT_ON;
5684
5685 /* Eat all trailing newlines */
5686 dest = expdest;
5687 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5688 STUNPUTC(dest);
5689 expdest = dest;
5690
5691 if (quoted == 0)
5692 recordregion(startloc, dest - (char *)stackblock(), 0);
5693 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
5694 (dest - (char *)stackblock()) - startloc,
5695 (dest - (char *)stackblock()) - startloc,
5696 stackblock() + startloc));
5697}
5698
5699#if ENABLE_ASH_MATH_SUPPORT
5700/*
5701 * Expand arithmetic expression. Backup to start of expression,
5702 * evaluate, place result in (backed up) result, adjust string position.
5703 */
5704static void
5705expari(int quotes)
5706{
5707 char *p, *start;
5708 int begoff;
5709 int flag;
5710 int len;
5711
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005712 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005713
5714 /*
5715 * This routine is slightly over-complicated for
5716 * efficiency. Next we scan backwards looking for the
5717 * start of arithmetic.
5718 */
5719 start = stackblock();
5720 p = expdest - 1;
5721 *p = '\0';
5722 p--;
5723 do {
5724 int esc;
5725
5726 while (*p != CTLARI) {
5727 p--;
5728#if DEBUG
5729 if (p < start) {
5730 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5731 }
5732#endif
5733 }
5734
5735 esc = esclen(start, p);
5736 if (!(esc % 2)) {
5737 break;
5738 }
5739
5740 p -= esc + 1;
5741 } while (1);
5742
5743 begoff = p - start;
5744
5745 removerecordregions(begoff);
5746
5747 flag = p[1];
5748
5749 expdest = p;
5750
5751 if (quotes)
5752 rmescapes(p + 2);
5753
5754 len = cvtnum(dash_arith(p + 2));
5755
5756 if (flag != '"')
5757 recordregion(begoff, begoff + len, 0);
5758}
5759#endif
5760
5761/* argstr needs it */
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005762static char *evalvar(char *p, int flag, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005763
5764/*
5765 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5766 * characters to allow for further processing. Otherwise treat
5767 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005768 *
5769 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5770 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5771 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005772 */
5773static void
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005774argstr(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005776 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005777 '=',
5778 ':',
5779 CTLQUOTEMARK,
5780 CTLENDVAR,
5781 CTLESC,
5782 CTLVAR,
5783 CTLBACKQ,
5784 CTLBACKQ | CTLQUOTE,
5785#if ENABLE_ASH_MATH_SUPPORT
5786 CTLENDARI,
5787#endif
5788 0
5789 };
5790 const char *reject = spclchars;
5791 int c;
5792 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
5793 int breakall = flag & EXP_WORD;
5794 int inquotes;
5795 size_t length;
5796 int startloc;
5797
5798 if (!(flag & EXP_VARTILDE)) {
5799 reject += 2;
5800 } else if (flag & EXP_VARTILDE2) {
5801 reject++;
5802 }
5803 inquotes = 0;
5804 length = 0;
5805 if (flag & EXP_TILDE) {
5806 char *q;
5807
5808 flag &= ~EXP_TILDE;
5809 tilde:
5810 q = p;
5811 if (*q == CTLESC && (flag & EXP_QWORD))
5812 q++;
5813 if (*q == '~')
5814 p = exptilde(p, q, flag);
5815 }
5816 start:
5817 startloc = expdest - (char *)stackblock();
5818 for (;;) {
5819 length += strcspn(p + length, reject);
5820 c = p[length];
5821 if (c && (!(c & 0x80)
5822#if ENABLE_ASH_MATH_SUPPORT
5823 || c == CTLENDARI
5824#endif
5825 )) {
5826 /* c == '=' || c == ':' || c == CTLENDARI */
5827 length++;
5828 }
5829 if (length > 0) {
5830 int newloc;
5831 expdest = stack_nputstr(p, length, expdest);
5832 newloc = expdest - (char *)stackblock();
5833 if (breakall && !inquotes && newloc > startloc) {
5834 recordregion(startloc, newloc, 0);
5835 }
5836 startloc = newloc;
5837 }
5838 p += length + 1;
5839 length = 0;
5840
5841 switch (c) {
5842 case '\0':
5843 goto breakloop;
5844 case '=':
5845 if (flag & EXP_VARTILDE2) {
5846 p--;
5847 continue;
5848 }
5849 flag |= EXP_VARTILDE2;
5850 reject++;
5851 /* fall through */
5852 case ':':
5853 /*
5854 * sort of a hack - expand tildes in variable
5855 * assignments (after the first '=' and after ':'s).
5856 */
5857 if (*--p == '~') {
5858 goto tilde;
5859 }
5860 continue;
5861 }
5862
5863 switch (c) {
5864 case CTLENDVAR: /* ??? */
5865 goto breakloop;
5866 case CTLQUOTEMARK:
5867 /* "$@" syntax adherence hack */
5868 if (
5869 !inquotes &&
5870 !memcmp(p, dolatstr, 4) &&
5871 (p[4] == CTLQUOTEMARK || (
5872 p[4] == CTLENDVAR &&
5873 p[5] == CTLQUOTEMARK
5874 ))
5875 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005876 p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005877 goto start;
5878 }
5879 inquotes = !inquotes;
5880 addquote:
5881 if (quotes) {
5882 p--;
5883 length++;
5884 startloc++;
5885 }
5886 break;
5887 case CTLESC:
5888 startloc++;
5889 length++;
5890 goto addquote;
5891 case CTLVAR:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005892 p = evalvar(p, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005893 goto start;
5894 case CTLBACKQ:
5895 c = 0;
5896 case CTLBACKQ|CTLQUOTE:
5897 expbackq(argbackq->n, c, quotes);
5898 argbackq = argbackq->next;
5899 goto start;
5900#if ENABLE_ASH_MATH_SUPPORT
5901 case CTLENDARI:
5902 p--;
5903 expari(quotes);
5904 goto start;
5905#endif
5906 }
5907 }
5908 breakloop:
5909 ;
5910}
5911
5912static char *
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00005913scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005914 int zero)
5915{
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005916// This commented out code was added by James Simmons <jsimmons@infradead.org>
5917// as part of a larger change when he added support for ${var/a/b}.
5918// However, it broke # and % operators:
5919//
5920//var=ababcdcd
5921// ok bad
5922//echo ${var#ab} abcdcd abcdcd
5923//echo ${var##ab} abcdcd abcdcd
5924//echo ${var#a*b} abcdcd ababcdcd (!)
5925//echo ${var##a*b} cdcd cdcd
5926//echo ${var#?} babcdcd ababcdcd (!)
5927//echo ${var##?} babcdcd babcdcd
5928//echo ${var#*} ababcdcd babcdcd (!)
5929//echo ${var##*}
5930//echo ${var%cd} ababcd ababcd
5931//echo ${var%%cd} ababcd abab (!)
5932//echo ${var%c*d} ababcd ababcd
5933//echo ${var%%c*d} abab ababcdcd (!)
5934//echo ${var%?} ababcdc ababcdc
5935//echo ${var%%?} ababcdc ababcdcd (!)
5936//echo ${var%*} ababcdcd ababcdcd
5937//echo ${var%%*}
5938//
5939// Commenting it back out helped. Remove it completely if it really
5940// is not needed.
5941
5942 char *loc, *loc2; //, *full;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005943 char c;
5944
5945 loc = startp;
5946 loc2 = rmesc;
5947 do {
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005948 int match; // = strlen(str);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005949 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005950
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951 c = *loc2;
5952 if (zero) {
5953 *loc2 = '\0';
5954 s = rmesc;
5955 }
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005956 match = pmatch(str, s); // this line was deleted
Denis Vlasenko92e13c22008-03-25 01:17:40 +00005957
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005958// // chop off end if its '*'
5959// full = strrchr(str, '*');
5960// if (full && full != str)
5961// match--;
5962//
5963// // If str starts with '*' replace with s.
5964// if ((*str == '*') && strlen(s) >= match) {
5965// full = xstrdup(s);
5966// strncpy(full+strlen(s)-match+1, str+1, match-1);
5967// } else
5968// full = xstrndup(str, match);
5969// match = strncmp(s, full, strlen(full));
5970// free(full);
5971//
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005972 *loc2 = c;
Denis Vlasenkoc7131c32008-04-14 01:59:53 +00005973 if (match) // if (!match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005974 return loc;
5975 if (quotes && *loc == CTLESC)
5976 loc++;
5977 loc++;
5978 loc2++;
5979 } while (c);
5980 return 0;
5981}
5982
5983static char *
5984scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5985 int zero)
5986{
5987 int esc = 0;
5988 char *loc;
5989 char *loc2;
5990
5991 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5992 int match;
5993 char c = *loc2;
5994 const char *s = loc2;
5995 if (zero) {
5996 *loc2 = '\0';
5997 s = rmesc;
5998 }
5999 match = pmatch(str, s);
6000 *loc2 = c;
6001 if (match)
6002 return loc;
6003 loc--;
6004 if (quotes) {
6005 if (--esc < 0) {
6006 esc = esclen(startp, loc);
6007 }
6008 if (esc % 2) {
6009 esc--;
6010 loc--;
6011 }
6012 }
6013 }
6014 return 0;
6015}
6016
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006017static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006018static void
6019varunset(const char *end, const char *var, const char *umsg, int varflags)
6020{
6021 const char *msg;
6022 const char *tail;
6023
6024 tail = nullstr;
6025 msg = "parameter not set";
6026 if (umsg) {
6027 if (*end == CTLENDVAR) {
6028 if (varflags & VSNUL)
6029 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006030 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006031 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006032 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006033 }
6034 ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
6035}
6036
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006037#if ENABLE_ASH_BASH_COMPAT
6038static char *
6039parse_sub_pattern(char *arg, int inquotes)
6040{
6041 char *idx, *repl = NULL;
6042 unsigned char c;
6043
Denis Vlasenko2659c632008-06-14 06:04:59 +00006044 idx = arg;
6045 while (1) {
6046 c = *arg;
6047 if (!c)
6048 break;
6049 if (c == '/') {
6050 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006051 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006052 repl = idx + 1;
6053 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006054 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006055 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006056 *idx++ = c;
6057 if (!inquotes && c == '\\' && arg[1] == '\\')
6058 arg++; /* skip both \\, not just first one */
6059 arg++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006060 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006061 *idx = c; /* NUL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006062
6063 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006064}
6065#endif /* ENABLE_ASH_BASH_COMPAT */
6066
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006067static const char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006068subevalvar(char *p, char *str, int strloc, int subtype,
6069 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006071 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006072 char *startp;
6073 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006074 char *rmesc, *rmescend;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006075 USE_ASH_BASH_COMPAT(char *repl = NULL;)
6076 USE_ASH_BASH_COMPAT(char null = '\0';)
6077 USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
6078 int saveherefd = herefd;
6079 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006080 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006081 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006082
6083 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006084 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6085 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006086 STPUTC('\0', expdest);
6087 herefd = saveherefd;
6088 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006089 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090
6091 switch (subtype) {
6092 case VSASSIGN:
6093 setvar(str, startp, 0);
6094 amount = startp - expdest;
6095 STADJUST(amount, expdest);
6096 return startp;
6097
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006098#if ENABLE_ASH_BASH_COMPAT
6099 case VSSUBSTR:
6100 loc = str = stackblock() + strloc;
6101// TODO: number() instead? It does error checking...
6102 pos = atoi(loc);
6103 len = str - startp - 1;
6104
6105 /* *loc != '\0', guaranteed by parser */
6106 if (quotes) {
6107 char *ptr;
6108
6109 /* We must adjust the length by the number of escapes we find. */
6110 for (ptr = startp; ptr < (str - 1); ptr++) {
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006111 if (*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006112 len--;
6113 ptr++;
6114 }
6115 }
6116 }
6117 orig_len = len;
6118
6119 if (*loc++ == ':') {
6120// TODO: number() instead? It does error checking...
6121 len = atoi(loc);
6122 } else {
6123 len = orig_len;
6124 while (*loc && *loc != ':')
6125 loc++;
6126 if (*loc++ == ':')
6127// TODO: number() instead? It does error checking...
6128 len = atoi(loc);
6129 }
6130 if (pos >= orig_len) {
6131 pos = 0;
6132 len = 0;
6133 }
6134 if (len > (orig_len - pos))
6135 len = orig_len - pos;
6136
6137 for (str = startp; pos; str++, pos--) {
6138 if (quotes && *str == CTLESC)
6139 str++;
6140 }
6141 for (loc = startp; len; len--) {
6142 if (quotes && *str == CTLESC)
6143 *loc++ = *str++;
6144 *loc++ = *str++;
6145 }
6146 *loc = '\0';
6147 amount = loc - expdest;
6148 STADJUST(amount, expdest);
6149 return loc;
6150#endif
6151
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006152 case VSQUESTION:
6153 varunset(p, str, startp, varflags);
6154 /* NOTREACHED */
6155 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006156 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006157
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006158 /* We'll comeback here if we grow the stack while handling
6159 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6160 * stack will need rebasing, and we'll need to remove our work
6161 * areas each time
6162 */
6163 USE_ASH_BASH_COMPAT(restart:)
6164
6165 amount = expdest - ((char *)stackblock() + resetloc);
6166 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006167 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168
6169 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006170 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006171 if (quotes) {
6172 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
6173 if (rmesc != startp) {
6174 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006175 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006176 }
6177 }
6178 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006179 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006180 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006181 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006183#if ENABLE_ASH_BASH_COMPAT
6184 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6185 char *idx, *end, *restart_detect;
6186
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006187 if (!repl) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006188 repl = parse_sub_pattern(str, varflags & VSQUOTE);
6189 if (!repl)
6190 repl = &null;
6191 }
6192
6193 /* If there's no pattern to match, return the expansion unmolested */
6194 if (*str == '\0')
6195 return 0;
6196
6197 len = 0;
6198 idx = startp;
6199 end = str - 1;
6200 while (idx < end) {
6201 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
6202 if (!loc) {
6203 /* No match, advance */
6204 restart_detect = stackblock();
6205 STPUTC(*idx, expdest);
6206 if (quotes && *idx == CTLESC) {
6207 idx++;
6208 len++;
6209 STPUTC(*idx, expdest);
6210 }
6211 if (stackblock() != restart_detect)
6212 goto restart;
6213 idx++;
6214 len++;
6215 rmesc++;
6216 continue;
6217 }
6218
6219 if (subtype == VSREPLACEALL) {
6220 while (idx < loc) {
6221 if (quotes && *idx == CTLESC)
6222 idx++;
6223 idx++;
6224 rmesc++;
6225 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006226 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006227 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006228 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006229
6230 for (loc = repl; *loc; loc++) {
6231 restart_detect = stackblock();
6232 STPUTC(*loc, expdest);
6233 if (stackblock() != restart_detect)
6234 goto restart;
6235 len++;
6236 }
6237
6238 if (subtype == VSREPLACE) {
6239 while (*idx) {
6240 restart_detect = stackblock();
6241 STPUTC(*idx, expdest);
6242 if (stackblock() != restart_detect)
6243 goto restart;
6244 len++;
6245 idx++;
6246 }
6247 break;
6248 }
6249 }
6250
6251 /* We've put the replaced text into a buffer at workloc, now
6252 * move it to the right place and adjust the stack.
6253 */
6254 startp = stackblock() + startloc;
6255 STPUTC('\0', expdest);
6256 memmove(startp, stackblock() + workloc, len);
6257 startp[len++] = '\0';
6258 amount = expdest - ((char *)stackblock() + startloc + len - 1);
6259 STADJUST(-amount, expdest);
6260 return startp;
6261 }
6262#endif /* ENABLE_ASH_BASH_COMPAT */
6263
6264 subtype -= VSTRIMRIGHT;
6265#if DEBUG
6266 if (subtype < 0 || subtype > 7)
6267 abort();
6268#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006269 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
6270 zero = subtype >> 1;
6271 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6272 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6273
6274 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6275 if (loc) {
6276 if (zero) {
6277 memmove(startp, loc, str - loc);
6278 loc = startp + (str - loc) - 1;
6279 }
6280 *loc = '\0';
6281 amount = loc - expdest;
6282 STADJUST(amount, expdest);
6283 }
6284 return loc;
6285}
6286
6287/*
6288 * Add the value of a specialized variable to the stack string.
6289 */
6290static ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006291varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006292{
6293 int num;
6294 char *p;
6295 int i;
6296 int sep = 0;
6297 int sepq = 0;
6298 ssize_t len = 0;
6299 char **ap;
6300 int syntax;
6301 int quoted = varflags & VSQUOTE;
6302 int subtype = varflags & VSTYPE;
6303 int quotes = flags & (EXP_FULL | EXP_CASE);
6304
6305 if (quoted && (flags & EXP_FULL))
6306 sep = 1 << CHAR_BIT;
6307
6308 syntax = quoted ? DQSYNTAX : BASESYNTAX;
6309 switch (*name) {
6310 case '$':
6311 num = rootpid;
6312 goto numvar;
6313 case '?':
6314 num = exitstatus;
6315 goto numvar;
6316 case '#':
6317 num = shellparam.nparam;
6318 goto numvar;
6319 case '!':
6320 num = backgndpid;
6321 if (num == 0)
6322 return -1;
6323 numvar:
6324 len = cvtnum(num);
6325 break;
6326 case '-':
6327 p = makestrspace(NOPTS, expdest);
6328 for (i = NOPTS - 1; i >= 0; i--) {
6329 if (optlist[i]) {
6330 USTPUTC(optletters(i), p);
6331 len++;
6332 }
6333 }
6334 expdest = p;
6335 break;
6336 case '@':
6337 if (sep)
6338 goto param;
6339 /* fall through */
6340 case '*':
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00006341 sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006342 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
6343 sepq = 1;
6344 param:
6345 ap = shellparam.p;
6346 if (!ap)
6347 return -1;
6348 while ((p = *ap++)) {
6349 size_t partlen;
6350
6351 partlen = strlen(p);
6352 len += partlen;
6353
6354 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6355 memtodest(p, partlen, syntax, quotes);
6356
6357 if (*ap && sep) {
6358 char *q;
6359
6360 len++;
6361 if (subtype == VSPLUS || subtype == VSLENGTH) {
6362 continue;
6363 }
6364 q = expdest;
6365 if (sepq)
6366 STPUTC(CTLESC, q);
6367 STPUTC(sep, q);
6368 expdest = q;
6369 }
6370 }
6371 return len;
6372 case '0':
6373 case '1':
6374 case '2':
6375 case '3':
6376 case '4':
6377 case '5':
6378 case '6':
6379 case '7':
6380 case '8':
6381 case '9':
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006382// TODO: number() instead? It does error checking...
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006383 num = atoi(name);
6384 if (num < 0 || num > shellparam.nparam)
6385 return -1;
6386 p = num ? shellparam.p[num - 1] : arg0;
6387 goto value;
6388 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006389 /* NB: name has form "VAR=..." */
6390
6391 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6392 * which should be considered before we check variables. */
6393 if (var_str_list) {
6394 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6395 p = NULL;
6396 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006397 char *str, *eq;
6398 str = var_str_list->text;
6399 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006400 if (!eq) /* stop at first non-assignment */
6401 break;
6402 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006403 if (name_len == (unsigned)(eq - str)
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006404 && strncmp(str, name, name_len) == 0) {
6405 p = eq;
6406 /* goto value; - WRONG! */
6407 /* think "A=1 A=2 B=$A" */
6408 }
6409 var_str_list = var_str_list->next;
6410 } while (var_str_list);
6411 if (p)
6412 goto value;
6413 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006414 p = lookupvar(name);
6415 value:
6416 if (!p)
6417 return -1;
6418
6419 len = strlen(p);
6420 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6421 memtodest(p, len, syntax, quotes);
6422 return len;
6423 }
6424
6425 if (subtype == VSPLUS || subtype == VSLENGTH)
6426 STADJUST(-len, expdest);
6427 return len;
6428}
6429
6430/*
6431 * Expand a variable, and return a pointer to the next character in the
6432 * input string.
6433 */
6434static char *
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006435evalvar(char *p, int flag, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006436{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006437 char varflags;
6438 char subtype;
6439 char quoted;
6440 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 char *var;
6442 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 int startloc;
6444 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006446 varflags = *p++;
6447 subtype = varflags & VSTYPE;
6448 quoted = varflags & VSQUOTE;
6449 var = p;
6450 easy = (!quoted || (*var == '@' && shellparam.nparam));
6451 startloc = expdest - (char *)stackblock();
6452 p = strchr(p, '=') + 1;
6453
6454 again:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006455 varlen = varvalue(var, varflags, flag, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006456 if (varflags & VSNUL)
6457 varlen--;
6458
6459 if (subtype == VSPLUS) {
6460 varlen = -1 - varlen;
6461 goto vsplus;
6462 }
6463
6464 if (subtype == VSMINUS) {
6465 vsplus:
6466 if (varlen < 0) {
6467 argstr(
6468 p, flag | EXP_TILDE |
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006469 (quoted ? EXP_QWORD : EXP_WORD),
6470 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006471 );
6472 goto end;
6473 }
6474 if (easy)
6475 goto record;
6476 goto end;
6477 }
6478
6479 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6480 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006481 if (subevalvar(p, var, /* strloc: */ 0,
6482 subtype, startloc, varflags,
6483 /* quotes: */ 0,
6484 var_str_list)
6485 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006486 varflags &= ~VSNUL;
6487 /*
6488 * Remove any recorded regions beyond
6489 * start of variable
6490 */
6491 removerecordregions(startloc);
6492 goto again;
6493 }
6494 goto end;
6495 }
6496 if (easy)
6497 goto record;
6498 goto end;
6499 }
6500
6501 if (varlen < 0 && uflag)
6502 varunset(p, var, 0, 0);
6503
6504 if (subtype == VSLENGTH) {
6505 cvtnum(varlen > 0 ? varlen : 0);
6506 goto record;
6507 }
6508
6509 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006510 if (easy)
6511 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006512 goto end;
6513 }
6514
6515#if DEBUG
6516 switch (subtype) {
6517 case VSTRIMLEFT:
6518 case VSTRIMLEFTMAX:
6519 case VSTRIMRIGHT:
6520 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006521#if ENABLE_ASH_BASH_COMPAT
6522 case VSSUBSTR:
6523 case VSREPLACE:
6524 case VSREPLACEALL:
6525#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006526 break;
6527 default:
6528 abort();
6529 }
6530#endif
6531
6532 if (varlen >= 0) {
6533 /*
6534 * Terminate the string and start recording the pattern
6535 * right after it
6536 */
6537 STPUTC('\0', expdest);
6538 patloc = expdest - (char *)stackblock();
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006539 if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
6540 startloc, varflags,
6541 /* quotes: */ flag & (EXP_FULL | EXP_CASE),
6542 var_str_list)
6543 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 int amount = expdest - (
6545 (char *)stackblock() + patloc - 1
6546 );
6547 STADJUST(-amount, expdest);
6548 }
6549 /* Remove any recorded regions beyond start of variable */
6550 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006551 record:
6552 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006553 }
6554
6555 end:
6556 if (subtype != VSNORMAL) { /* skip to end of alternative */
6557 int nesting = 1;
6558 for (;;) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006559 char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006560 if (c == CTLESC)
6561 p++;
6562 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6563 if (varlen >= 0)
6564 argbackq = argbackq->next;
6565 } else if (c == CTLVAR) {
6566 if ((*p++ & VSTYPE) != VSNORMAL)
6567 nesting++;
6568 } else if (c == CTLENDVAR) {
6569 if (--nesting == 0)
6570 break;
6571 }
6572 }
6573 }
6574 return p;
6575}
6576
6577/*
6578 * Break the argument string into pieces based upon IFS and add the
6579 * strings to the argument list. The regions of the string to be
6580 * searched for IFS characters have been stored by recordregion.
6581 */
6582static void
6583ifsbreakup(char *string, struct arglist *arglist)
6584{
6585 struct ifsregion *ifsp;
6586 struct strlist *sp;
6587 char *start;
6588 char *p;
6589 char *q;
6590 const char *ifs, *realifs;
6591 int ifsspc;
6592 int nulonly;
6593
6594 start = string;
6595 if (ifslastp != NULL) {
6596 ifsspc = 0;
6597 nulonly = 0;
6598 realifs = ifsset() ? ifsval() : defifs;
6599 ifsp = &ifsfirst;
6600 do {
6601 p = string + ifsp->begoff;
6602 nulonly = ifsp->nulonly;
6603 ifs = nulonly ? nullstr : realifs;
6604 ifsspc = 0;
6605 while (p < string + ifsp->endoff) {
6606 q = p;
6607 if (*p == CTLESC)
6608 p++;
6609 if (!strchr(ifs, *p)) {
6610 p++;
6611 continue;
6612 }
6613 if (!nulonly)
6614 ifsspc = (strchr(defifs, *p) != NULL);
6615 /* Ignore IFS whitespace at start */
6616 if (q == start && ifsspc) {
6617 p++;
6618 start = p;
6619 continue;
6620 }
6621 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006622 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006623 sp->text = start;
6624 *arglist->lastp = sp;
6625 arglist->lastp = &sp->next;
6626 p++;
6627 if (!nulonly) {
6628 for (;;) {
6629 if (p >= string + ifsp->endoff) {
6630 break;
6631 }
6632 q = p;
6633 if (*p == CTLESC)
6634 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006635 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006636 p = q;
6637 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006638 }
6639 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006640 if (ifsspc) {
6641 p++;
6642 ifsspc = 0;
6643 } else {
6644 p = q;
6645 break;
6646 }
6647 } else
6648 p++;
6649 }
6650 }
6651 start = p;
6652 } /* while */
6653 ifsp = ifsp->next;
6654 } while (ifsp != NULL);
6655 if (nulonly)
6656 goto add;
6657 }
6658
6659 if (!*start)
6660 return;
6661
6662 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006663 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006664 sp->text = start;
6665 *arglist->lastp = sp;
6666 arglist->lastp = &sp->next;
6667}
6668
6669static void
6670ifsfree(void)
6671{
6672 struct ifsregion *p;
6673
6674 INT_OFF;
6675 p = ifsfirst.next;
6676 do {
6677 struct ifsregion *ifsp;
6678 ifsp = p->next;
6679 free(p);
6680 p = ifsp;
6681 } while (p);
6682 ifslastp = NULL;
6683 ifsfirst.next = NULL;
6684 INT_ON;
6685}
6686
6687/*
6688 * Add a file name to the list.
6689 */
6690static void
6691addfname(const char *name)
6692{
6693 struct strlist *sp;
6694
Denis Vlasenko597906c2008-02-20 16:38:54 +00006695 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006696 sp->text = ststrdup(name);
6697 *exparg.lastp = sp;
6698 exparg.lastp = &sp->next;
6699}
6700
6701static char *expdir;
6702
6703/*
6704 * Do metacharacter (i.e. *, ?, [...]) expansion.
6705 */
6706static void
6707expmeta(char *enddir, char *name)
6708{
6709 char *p;
6710 const char *cp;
6711 char *start;
6712 char *endname;
6713 int metaflag;
6714 struct stat statb;
6715 DIR *dirp;
6716 struct dirent *dp;
6717 int atend;
6718 int matchdot;
6719
6720 metaflag = 0;
6721 start = name;
6722 for (p = name; *p; p++) {
6723 if (*p == '*' || *p == '?')
6724 metaflag = 1;
6725 else if (*p == '[') {
6726 char *q = p + 1;
6727 if (*q == '!')
6728 q++;
6729 for (;;) {
6730 if (*q == '\\')
6731 q++;
6732 if (*q == '/' || *q == '\0')
6733 break;
6734 if (*++q == ']') {
6735 metaflag = 1;
6736 break;
6737 }
6738 }
6739 } else if (*p == '\\')
6740 p++;
6741 else if (*p == '/') {
6742 if (metaflag)
6743 goto out;
6744 start = p + 1;
6745 }
6746 }
6747 out:
6748 if (metaflag == 0) { /* we've reached the end of the file name */
6749 if (enddir != expdir)
6750 metaflag++;
6751 p = name;
6752 do {
6753 if (*p == '\\')
6754 p++;
6755 *enddir++ = *p;
6756 } while (*p++);
6757 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
6758 addfname(expdir);
6759 return;
6760 }
6761 endname = p;
6762 if (name < start) {
6763 p = name;
6764 do {
6765 if (*p == '\\')
6766 p++;
6767 *enddir++ = *p++;
6768 } while (p < start);
6769 }
6770 if (enddir == expdir) {
6771 cp = ".";
6772 } else if (enddir == expdir + 1 && *expdir == '/') {
6773 cp = "/";
6774 } else {
6775 cp = expdir;
6776 enddir[-1] = '\0';
6777 }
6778 dirp = opendir(cp);
6779 if (dirp == NULL)
6780 return;
6781 if (enddir != expdir)
6782 enddir[-1] = '/';
6783 if (*endname == 0) {
6784 atend = 1;
6785 } else {
6786 atend = 0;
6787 *endname++ = '\0';
6788 }
6789 matchdot = 0;
6790 p = start;
6791 if (*p == '\\')
6792 p++;
6793 if (*p == '.')
6794 matchdot++;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00006795 while (!intpending && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006796 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006797 continue;
6798 if (pmatch(start, dp->d_name)) {
6799 if (atend) {
6800 strcpy(enddir, dp->d_name);
6801 addfname(expdir);
6802 } else {
6803 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
6804 continue;
6805 p[-1] = '/';
6806 expmeta(p, endname);
6807 }
6808 }
6809 }
6810 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00006811 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006812 endname[-1] = '/';
6813}
6814
6815static struct strlist *
6816msort(struct strlist *list, int len)
6817{
6818 struct strlist *p, *q = NULL;
6819 struct strlist **lpp;
6820 int half;
6821 int n;
6822
6823 if (len <= 1)
6824 return list;
6825 half = len >> 1;
6826 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006827 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006828 q = p;
6829 p = p->next;
6830 }
6831 q->next = NULL; /* terminate first half of list */
6832 q = msort(list, half); /* sort first half of list */
6833 p = msort(p, len - half); /* sort second half */
6834 lpp = &list;
6835 for (;;) {
6836#if ENABLE_LOCALE_SUPPORT
6837 if (strcoll(p->text, q->text) < 0)
6838#else
6839 if (strcmp(p->text, q->text) < 0)
6840#endif
6841 {
6842 *lpp = p;
6843 lpp = &p->next;
6844 p = *lpp;
6845 if (p == NULL) {
6846 *lpp = q;
6847 break;
6848 }
6849 } else {
6850 *lpp = q;
6851 lpp = &q->next;
6852 q = *lpp;
6853 if (q == NULL) {
6854 *lpp = p;
6855 break;
6856 }
6857 }
6858 }
6859 return list;
6860}
6861
6862/*
6863 * Sort the results of file name expansion. It calculates the number of
6864 * strings to sort and then calls msort (short for merge sort) to do the
6865 * work.
6866 */
6867static struct strlist *
6868expsort(struct strlist *str)
6869{
6870 int len;
6871 struct strlist *sp;
6872
6873 len = 0;
6874 for (sp = str; sp; sp = sp->next)
6875 len++;
6876 return msort(str, len);
6877}
6878
6879static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00006880expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006882 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006883 '*', '?', '[', 0
6884 };
6885 /* TODO - EXP_REDIR */
6886
6887 while (str) {
6888 struct strlist **savelastp;
6889 struct strlist *sp;
6890 char *p;
6891
6892 if (fflag)
6893 goto nometa;
6894 if (!strpbrk(str->text, metachars))
6895 goto nometa;
6896 savelastp = exparg.lastp;
6897
6898 INT_OFF;
6899 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
6900 {
6901 int i = strlen(str->text);
6902 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
6903 }
6904
6905 expmeta(expdir, p);
6906 free(expdir);
6907 if (p != str->text)
6908 free(p);
6909 INT_ON;
6910 if (exparg.lastp == savelastp) {
6911 /*
6912 * no matches
6913 */
6914 nometa:
6915 *exparg.lastp = str;
6916 rmescapes(str->text);
6917 exparg.lastp = &str->next;
6918 } else {
6919 *exparg.lastp = NULL;
6920 *savelastp = sp = expsort(*savelastp);
6921 while (sp->next != NULL)
6922 sp = sp->next;
6923 exparg.lastp = &sp->next;
6924 }
6925 str = str->next;
6926 }
6927}
6928
6929/*
6930 * Perform variable substitution and command substitution on an argument,
6931 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
6932 * perform splitting and file name expansion. When arglist is NULL, perform
6933 * here document expansion.
6934 */
6935static void
6936expandarg(union node *arg, struct arglist *arglist, int flag)
6937{
6938 struct strlist *sp;
6939 char *p;
6940
6941 argbackq = arg->narg.backquote;
6942 STARTSTACKSTR(expdest);
6943 ifsfirst.next = NULL;
6944 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006945 argstr(arg->narg.text, flag,
6946 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006947 p = _STPUTC('\0', expdest);
6948 expdest = p - 1;
6949 if (arglist == NULL) {
6950 return; /* here document expanded */
6951 }
6952 p = grabstackstr(p);
6953 exparg.lastp = &exparg.list;
6954 /*
6955 * TODO - EXP_REDIR
6956 */
6957 if (flag & EXP_FULL) {
6958 ifsbreakup(p, &exparg);
6959 *exparg.lastp = NULL;
6960 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00006961 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006962 } else {
6963 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
6964 rmescapes(p);
Denis Vlasenko597906c2008-02-20 16:38:54 +00006965 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006966 sp->text = p;
6967 *exparg.lastp = sp;
6968 exparg.lastp = &sp->next;
6969 }
6970 if (ifsfirst.next)
6971 ifsfree();
6972 *exparg.lastp = NULL;
6973 if (exparg.list) {
6974 *arglist->lastp = exparg.list;
6975 arglist->lastp = exparg.lastp;
6976 }
6977}
6978
6979/*
6980 * Expand shell variables and backquotes inside a here document.
6981 */
6982static void
6983expandhere(union node *arg, int fd)
6984{
6985 herefd = fd;
6986 expandarg(arg, (struct arglist *)NULL, 0);
6987 full_write(fd, stackblock(), expdest - (char *)stackblock());
6988}
6989
6990/*
6991 * Returns true if the pattern matches the string.
6992 */
6993static int
6994patmatch(char *pattern, const char *string)
6995{
6996 return pmatch(preglob(pattern, 0, 0), string);
6997}
6998
6999/*
7000 * See if a pattern matches in a case statement.
7001 */
7002static int
7003casematch(union node *pattern, char *val)
7004{
7005 struct stackmark smark;
7006 int result;
7007
7008 setstackmark(&smark);
7009 argbackq = pattern->narg.backquote;
7010 STARTSTACKSTR(expdest);
7011 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007012 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7013 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007014 STACKSTRNUL(expdest);
7015 result = patmatch(stackblock(), val);
7016 popstackmark(&smark);
7017 return result;
7018}
7019
7020
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007021/* ============ find_command */
7022
7023struct builtincmd {
7024 const char *name;
7025 int (*builtin)(int, char **);
7026 /* unsigned flags; */
7027};
7028#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007029/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007030 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007031#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007032#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007033
7034struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007035 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007036 union param {
7037 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007038 /* index >= 0 for commands without path (slashes) */
7039 /* (TODO: what exactly does the value mean? PATH position?) */
7040 /* index == -1 for commands with slashes */
7041 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007042 const struct builtincmd *cmd;
7043 struct funcnode *func;
7044 } u;
7045};
7046/* values of cmdtype */
7047#define CMDUNKNOWN -1 /* no entry in table for command */
7048#define CMDNORMAL 0 /* command is an executable program */
7049#define CMDFUNCTION 1 /* command is a shell function */
7050#define CMDBUILTIN 2 /* command is a shell builtin */
7051
7052/* action to find_command() */
7053#define DO_ERR 0x01 /* prints errors */
7054#define DO_ABS 0x02 /* checks absolute paths */
7055#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7056#define DO_ALTPATH 0x08 /* using alternate path */
7057#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7058
7059static void find_command(char *, struct cmdentry *, int, const char *);
7060
7061
7062/* ============ Hashing commands */
7063
7064/*
7065 * When commands are first encountered, they are entered in a hash table.
7066 * This ensures that a full path search will not have to be done for them
7067 * on each invocation.
7068 *
7069 * We should investigate converting to a linear search, even though that
7070 * would make the command name "hash" a misnomer.
7071 */
7072
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007073struct tblentry {
7074 struct tblentry *next; /* next entry in hash chain */
7075 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007076 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007077 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007078 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007079};
7080
Denis Vlasenko01631112007-12-16 17:20:38 +00007081static struct tblentry **cmdtable;
7082#define INIT_G_cmdtable() do { \
7083 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7084} while (0)
7085
7086static int builtinloc = -1; /* index in path of %builtin, or -1 */
7087
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007088
7089static void
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007090tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007091{
7092 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007093
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007094#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007095 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007096 if (APPLET_IS_NOEXEC(applet_no)) {
7097 while (*envp)
7098 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007099 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007100 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007101 /* re-exec ourselves with the new arguments */
7102 execve(bb_busybox_exec_path, argv, envp);
7103 /* If they called chroot or otherwise made the binary no longer
7104 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007105 }
7106#endif
7107
7108 repeat:
7109#ifdef SYSV
7110 do {
7111 execve(cmd, argv, envp);
7112 } while (errno == EINTR);
7113#else
7114 execve(cmd, argv, envp);
7115#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007116 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007117 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007118 return;
7119 }
7120 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007121 char **ap;
7122 char **new;
7123
7124 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007125 continue;
7126 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007127 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007128 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007129 ap += 2;
7130 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007131 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007132 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007133 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007134 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007135 goto repeat;
7136 }
7137}
7138
7139/*
7140 * Exec a program. Never returns. If you change this routine, you may
7141 * have to change the find_command routine as well.
7142 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007143static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007144static void
7145shellexec(char **argv, const char *path, int idx)
7146{
7147 char *cmdname;
7148 int e;
7149 char **envp;
7150 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007151#if ENABLE_FEATURE_SH_STANDALONE
7152 int applet_no = -1;
7153#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007154
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007155 clearredir(/*drop:*/ 1);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007156 envp = listvars(VEXPORT, VUNSET, 0);
7157 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007158#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007159 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007160#endif
7161 ) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007162 tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007163 e = errno;
7164 } else {
7165 e = ENOENT;
7166 while ((cmdname = padvance(&path, argv[0])) != NULL) {
7167 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007168 tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007169 if (errno != ENOENT && errno != ENOTDIR)
7170 e = errno;
7171 }
7172 stunalloc(cmdname);
7173 }
7174 }
7175
7176 /* Map to POSIX errors */
7177 switch (e) {
7178 case EACCES:
7179 exerrno = 126;
7180 break;
7181 case ENOENT:
7182 exerrno = 127;
7183 break;
7184 default:
7185 exerrno = 2;
7186 break;
7187 }
7188 exitstatus = exerrno;
7189 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007190 argv[0], e, suppressint));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007191 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7192 /* NOTREACHED */
7193}
7194
7195static void
7196printentry(struct tblentry *cmdp)
7197{
7198 int idx;
7199 const char *path;
7200 char *name;
7201
7202 idx = cmdp->param.index;
7203 path = pathval();
7204 do {
7205 name = padvance(&path, cmdp->cmdname);
7206 stunalloc(name);
7207 } while (--idx >= 0);
7208 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7209}
7210
7211/*
7212 * Clear out command entries. The argument specifies the first entry in
7213 * PATH which has changed.
7214 */
7215static void
7216clearcmdentry(int firstchange)
7217{
7218 struct tblentry **tblp;
7219 struct tblentry **pp;
7220 struct tblentry *cmdp;
7221
7222 INT_OFF;
7223 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7224 pp = tblp;
7225 while ((cmdp = *pp) != NULL) {
7226 if ((cmdp->cmdtype == CMDNORMAL &&
7227 cmdp->param.index >= firstchange)
7228 || (cmdp->cmdtype == CMDBUILTIN &&
7229 builtinloc >= firstchange)
7230 ) {
7231 *pp = cmdp->next;
7232 free(cmdp);
7233 } else {
7234 pp = &cmdp->next;
7235 }
7236 }
7237 }
7238 INT_ON;
7239}
7240
7241/*
7242 * Locate a command in the command hash table. If "add" is nonzero,
7243 * add the command to the table if it is not already present. The
7244 * variable "lastcmdentry" is set to point to the address of the link
7245 * pointing to the entry, so that delete_cmd_entry can delete the
7246 * entry.
7247 *
7248 * Interrupts must be off if called with add != 0.
7249 */
7250static struct tblentry **lastcmdentry;
7251
7252static struct tblentry *
7253cmdlookup(const char *name, int add)
7254{
7255 unsigned int hashval;
7256 const char *p;
7257 struct tblentry *cmdp;
7258 struct tblentry **pp;
7259
7260 p = name;
7261 hashval = (unsigned char)*p << 4;
7262 while (*p)
7263 hashval += (unsigned char)*p++;
7264 hashval &= 0x7FFF;
7265 pp = &cmdtable[hashval % CMDTABLESIZE];
7266 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7267 if (strcmp(cmdp->cmdname, name) == 0)
7268 break;
7269 pp = &cmdp->next;
7270 }
7271 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007272 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7273 + strlen(name)
7274 /* + 1 - already done because
7275 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007276 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007277 cmdp->cmdtype = CMDUNKNOWN;
7278 strcpy(cmdp->cmdname, name);
7279 }
7280 lastcmdentry = pp;
7281 return cmdp;
7282}
7283
7284/*
7285 * Delete the command entry returned on the last lookup.
7286 */
7287static void
7288delete_cmd_entry(void)
7289{
7290 struct tblentry *cmdp;
7291
7292 INT_OFF;
7293 cmdp = *lastcmdentry;
7294 *lastcmdentry = cmdp->next;
7295 if (cmdp->cmdtype == CMDFUNCTION)
7296 freefunc(cmdp->param.func);
7297 free(cmdp);
7298 INT_ON;
7299}
7300
7301/*
7302 * Add a new command entry, replacing any existing command entry for
7303 * the same name - except special builtins.
7304 */
7305static void
7306addcmdentry(char *name, struct cmdentry *entry)
7307{
7308 struct tblentry *cmdp;
7309
7310 cmdp = cmdlookup(name, 1);
7311 if (cmdp->cmdtype == CMDFUNCTION) {
7312 freefunc(cmdp->param.func);
7313 }
7314 cmdp->cmdtype = entry->cmdtype;
7315 cmdp->param = entry->u;
7316 cmdp->rehash = 0;
7317}
7318
7319static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007320hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007321{
7322 struct tblentry **pp;
7323 struct tblentry *cmdp;
7324 int c;
7325 struct cmdentry entry;
7326 char *name;
7327
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007328 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007329 clearcmdentry(0);
7330 return 0;
7331 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007332
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333 if (*argptr == NULL) {
7334 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7335 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7336 if (cmdp->cmdtype == CMDNORMAL)
7337 printentry(cmdp);
7338 }
7339 }
7340 return 0;
7341 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007342
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007343 c = 0;
7344 while ((name = *argptr) != NULL) {
7345 cmdp = cmdlookup(name, 0);
7346 if (cmdp != NULL
7347 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007348 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7349 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007351 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007352 find_command(name, &entry, DO_ERR, pathval());
7353 if (entry.cmdtype == CMDUNKNOWN)
7354 c = 1;
7355 argptr++;
7356 }
7357 return c;
7358}
7359
7360/*
7361 * Called when a cd is done. Marks all commands so the next time they
7362 * are executed they will be rehashed.
7363 */
7364static void
7365hashcd(void)
7366{
7367 struct tblentry **pp;
7368 struct tblentry *cmdp;
7369
7370 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7371 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007372 if (cmdp->cmdtype == CMDNORMAL
7373 || (cmdp->cmdtype == CMDBUILTIN
7374 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
7375 && builtinloc > 0)
7376 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007377 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007378 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379 }
7380 }
7381}
7382
7383/*
7384 * Fix command hash table when PATH changed.
7385 * Called before PATH is changed. The argument is the new value of PATH;
7386 * pathval() still returns the old value at this point.
7387 * Called with interrupts off.
7388 */
7389static void
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007390changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007392 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007394 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007395 int idx_bltin;
7396
7397 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398 firstchange = 9999; /* assume no change */
7399 idx = 0;
7400 idx_bltin = -1;
7401 for (;;) {
7402 if (*old != *new) {
7403 firstchange = idx;
7404 if ((*old == '\0' && *new == ':')
7405 || (*old == ':' && *new == '\0'))
7406 firstchange++;
7407 old = new; /* ignore subsequent differences */
7408 }
7409 if (*new == '\0')
7410 break;
7411 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7412 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007413 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007414 idx++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415 new++, old++;
7416 }
7417 if (builtinloc < 0 && idx_bltin >= 0)
7418 builtinloc = idx_bltin; /* zap builtins */
7419 if (builtinloc >= 0 && idx_bltin < 0)
7420 firstchange = 0;
7421 clearcmdentry(firstchange);
7422 builtinloc = idx_bltin;
7423}
7424
7425#define TEOF 0
7426#define TNL 1
7427#define TREDIR 2
7428#define TWORD 3
7429#define TSEMI 4
7430#define TBACKGND 5
7431#define TAND 6
7432#define TOR 7
7433#define TPIPE 8
7434#define TLP 9
7435#define TRP 10
7436#define TENDCASE 11
7437#define TENDBQUOTE 12
7438#define TNOT 13
7439#define TCASE 14
7440#define TDO 15
7441#define TDONE 16
7442#define TELIF 17
7443#define TELSE 18
7444#define TESAC 19
7445#define TFI 20
7446#define TFOR 21
7447#define TIF 22
7448#define TIN 23
7449#define TTHEN 24
7450#define TUNTIL 25
7451#define TWHILE 26
7452#define TBEGIN 27
7453#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007454typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007455
7456/* first char is indicating which tokens mark the end of a list */
7457static const char *const tokname_array[] = {
7458 "\1end of file",
7459 "\0newline",
7460 "\0redirection",
7461 "\0word",
7462 "\0;",
7463 "\0&",
7464 "\0&&",
7465 "\0||",
7466 "\0|",
7467 "\0(",
7468 "\1)",
7469 "\1;;",
7470 "\1`",
7471#define KWDOFFSET 13
7472 /* the following are keywords */
7473 "\0!",
7474 "\0case",
7475 "\1do",
7476 "\1done",
7477 "\1elif",
7478 "\1else",
7479 "\1esac",
7480 "\1fi",
7481 "\0for",
7482 "\0if",
7483 "\0in",
7484 "\1then",
7485 "\0until",
7486 "\0while",
7487 "\0{",
7488 "\1}",
7489};
7490
7491static const char *
7492tokname(int tok)
7493{
7494 static char buf[16];
7495
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007496//try this:
7497//if (tok < TSEMI) return tokname_array[tok] + 1;
7498//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
7499//return buf;
7500
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007501 if (tok >= TSEMI)
7502 buf[0] = '"';
7503 sprintf(buf + (tok >= TSEMI), "%s%c",
7504 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
7505 return buf;
7506}
7507
7508/* Wrapper around strcmp for qsort/bsearch/... */
7509static int
7510pstrcmp(const void *a, const void *b)
7511{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007512 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007513}
7514
7515static const char *const *
7516findkwd(const char *s)
7517{
7518 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007519 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7520 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007521}
7522
7523/*
7524 * Locate and print what a word is...
7525 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007526static int
7527describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007528{
7529 struct cmdentry entry;
7530 struct tblentry *cmdp;
7531#if ENABLE_ASH_ALIAS
7532 const struct alias *ap;
7533#endif
7534 const char *path = pathval();
7535
7536 if (describe_command_verbose) {
7537 out1str(command);
7538 }
7539
7540 /* First look at the keywords */
7541 if (findkwd(command)) {
7542 out1str(describe_command_verbose ? " is a shell keyword" : command);
7543 goto out;
7544 }
7545
7546#if ENABLE_ASH_ALIAS
7547 /* Then look at the aliases */
7548 ap = lookupalias(command, 0);
7549 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007550 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007551 out1str("alias ");
7552 printalias(ap);
7553 return 0;
7554 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007555 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007556 goto out;
7557 }
7558#endif
7559 /* Then check if it is a tracked alias */
7560 cmdp = cmdlookup(command, 0);
7561 if (cmdp != NULL) {
7562 entry.cmdtype = cmdp->cmdtype;
7563 entry.u = cmdp->param;
7564 } else {
7565 /* Finally use brute force */
7566 find_command(command, &entry, DO_ABS, path);
7567 }
7568
7569 switch (entry.cmdtype) {
7570 case CMDNORMAL: {
7571 int j = entry.u.index;
7572 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007573 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007574 p = command;
7575 } else {
7576 do {
7577 p = padvance(&path, command);
7578 stunalloc(p);
7579 } while (--j >= 0);
7580 }
7581 if (describe_command_verbose) {
7582 out1fmt(" is%s %s",
7583 (cmdp ? " a tracked alias for" : nullstr), p
7584 );
7585 } else {
7586 out1str(p);
7587 }
7588 break;
7589 }
7590
7591 case CMDFUNCTION:
7592 if (describe_command_verbose) {
7593 out1str(" is a shell function");
7594 } else {
7595 out1str(command);
7596 }
7597 break;
7598
7599 case CMDBUILTIN:
7600 if (describe_command_verbose) {
7601 out1fmt(" is a %sshell builtin",
7602 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7603 "special " : nullstr
7604 );
7605 } else {
7606 out1str(command);
7607 }
7608 break;
7609
7610 default:
7611 if (describe_command_verbose) {
7612 out1str(": not found\n");
7613 }
7614 return 127;
7615 }
7616 out:
7617 outstr("\n", stdout);
7618 return 0;
7619}
7620
7621static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007622typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007623{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007624 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007625 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007626 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007627
Denis Vlasenko46846e22007-05-20 13:08:31 +00007628 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007629 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007630 i++;
7631 verbose = 0;
7632 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007633 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007634 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007635 }
7636 return err;
7637}
7638
7639#if ENABLE_ASH_CMDCMD
7640static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007641commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007642{
7643 int c;
7644 enum {
7645 VERIFY_BRIEF = 1,
7646 VERIFY_VERBOSE = 2,
7647 } verify = 0;
7648
7649 while ((c = nextopt("pvV")) != '\0')
7650 if (c == 'V')
7651 verify |= VERIFY_VERBOSE;
7652 else if (c == 'v')
7653 verify |= VERIFY_BRIEF;
7654#if DEBUG
7655 else if (c != 'p')
7656 abort();
7657#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007658 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7659 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007660 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007661 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662
7663 return 0;
7664}
7665#endif
7666
7667
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007668/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007669
Denis Vlasenko340299a2008-11-21 10:36:36 +00007670static int funcblocksize; /* size of structures in function */
7671static int funcstringsize; /* size of strings in node */
7672static void *funcblock; /* block to allocate function from */
7673static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007674
Eric Andersencb57d552001-06-28 07:25:16 +00007675/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007676#define EV_EXIT 01 /* exit after evaluating tree */
7677#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007678#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007679
Denis Vlasenko340299a2008-11-21 10:36:36 +00007680static const short nodesize[N_NUMBER] = {
7681 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7682 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7683 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7684 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7685 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7686 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7687 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7688 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7689 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7690 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7691 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7692 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7693 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7694 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7695 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7696 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7697 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007698#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007699 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007700#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007701 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7702 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7703 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7704 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7705 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7706 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7707 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7708 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7709 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007710};
7711
7712static void calcsize(union node *n);
7713
7714static void
7715sizenodelist(struct nodelist *lp)
7716{
7717 while (lp) {
7718 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7719 calcsize(lp->n);
7720 lp = lp->next;
7721 }
7722}
7723
7724static void
7725calcsize(union node *n)
7726{
7727 if (n == NULL)
7728 return;
7729 funcblocksize += nodesize[n->type];
7730 switch (n->type) {
7731 case NCMD:
7732 calcsize(n->ncmd.redirect);
7733 calcsize(n->ncmd.args);
7734 calcsize(n->ncmd.assign);
7735 break;
7736 case NPIPE:
7737 sizenodelist(n->npipe.cmdlist);
7738 break;
7739 case NREDIR:
7740 case NBACKGND:
7741 case NSUBSHELL:
7742 calcsize(n->nredir.redirect);
7743 calcsize(n->nredir.n);
7744 break;
7745 case NAND:
7746 case NOR:
7747 case NSEMI:
7748 case NWHILE:
7749 case NUNTIL:
7750 calcsize(n->nbinary.ch2);
7751 calcsize(n->nbinary.ch1);
7752 break;
7753 case NIF:
7754 calcsize(n->nif.elsepart);
7755 calcsize(n->nif.ifpart);
7756 calcsize(n->nif.test);
7757 break;
7758 case NFOR:
7759 funcstringsize += strlen(n->nfor.var) + 1;
7760 calcsize(n->nfor.body);
7761 calcsize(n->nfor.args);
7762 break;
7763 case NCASE:
7764 calcsize(n->ncase.cases);
7765 calcsize(n->ncase.expr);
7766 break;
7767 case NCLIST:
7768 calcsize(n->nclist.body);
7769 calcsize(n->nclist.pattern);
7770 calcsize(n->nclist.next);
7771 break;
7772 case NDEFUN:
7773 case NARG:
7774 sizenodelist(n->narg.backquote);
7775 funcstringsize += strlen(n->narg.text) + 1;
7776 calcsize(n->narg.next);
7777 break;
7778 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007779#if ENABLE_ASH_BASH_COMPAT
7780 case NTO2:
7781#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007782 case NCLOBBER:
7783 case NFROM:
7784 case NFROMTO:
7785 case NAPPEND:
7786 calcsize(n->nfile.fname);
7787 calcsize(n->nfile.next);
7788 break;
7789 case NTOFD:
7790 case NFROMFD:
7791 calcsize(n->ndup.vname);
7792 calcsize(n->ndup.next);
7793 break;
7794 case NHERE:
7795 case NXHERE:
7796 calcsize(n->nhere.doc);
7797 calcsize(n->nhere.next);
7798 break;
7799 case NNOT:
7800 calcsize(n->nnot.com);
7801 break;
7802 };
7803}
7804
7805static char *
7806nodeckstrdup(char *s)
7807{
7808 char *rtn = funcstring;
7809
7810 strcpy(funcstring, s);
7811 funcstring += strlen(s) + 1;
7812 return rtn;
7813}
7814
7815static union node *copynode(union node *);
7816
7817static struct nodelist *
7818copynodelist(struct nodelist *lp)
7819{
7820 struct nodelist *start;
7821 struct nodelist **lpp;
7822
7823 lpp = &start;
7824 while (lp) {
7825 *lpp = funcblock;
7826 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
7827 (*lpp)->n = copynode(lp->n);
7828 lp = lp->next;
7829 lpp = &(*lpp)->next;
7830 }
7831 *lpp = NULL;
7832 return start;
7833}
7834
7835static union node *
7836copynode(union node *n)
7837{
7838 union node *new;
7839
7840 if (n == NULL)
7841 return NULL;
7842 new = funcblock;
7843 funcblock = (char *) funcblock + nodesize[n->type];
7844
7845 switch (n->type) {
7846 case NCMD:
7847 new->ncmd.redirect = copynode(n->ncmd.redirect);
7848 new->ncmd.args = copynode(n->ncmd.args);
7849 new->ncmd.assign = copynode(n->ncmd.assign);
7850 break;
7851 case NPIPE:
7852 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007853 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007854 break;
7855 case NREDIR:
7856 case NBACKGND:
7857 case NSUBSHELL:
7858 new->nredir.redirect = copynode(n->nredir.redirect);
7859 new->nredir.n = copynode(n->nredir.n);
7860 break;
7861 case NAND:
7862 case NOR:
7863 case NSEMI:
7864 case NWHILE:
7865 case NUNTIL:
7866 new->nbinary.ch2 = copynode(n->nbinary.ch2);
7867 new->nbinary.ch1 = copynode(n->nbinary.ch1);
7868 break;
7869 case NIF:
7870 new->nif.elsepart = copynode(n->nif.elsepart);
7871 new->nif.ifpart = copynode(n->nif.ifpart);
7872 new->nif.test = copynode(n->nif.test);
7873 break;
7874 case NFOR:
7875 new->nfor.var = nodeckstrdup(n->nfor.var);
7876 new->nfor.body = copynode(n->nfor.body);
7877 new->nfor.args = copynode(n->nfor.args);
7878 break;
7879 case NCASE:
7880 new->ncase.cases = copynode(n->ncase.cases);
7881 new->ncase.expr = copynode(n->ncase.expr);
7882 break;
7883 case NCLIST:
7884 new->nclist.body = copynode(n->nclist.body);
7885 new->nclist.pattern = copynode(n->nclist.pattern);
7886 new->nclist.next = copynode(n->nclist.next);
7887 break;
7888 case NDEFUN:
7889 case NARG:
7890 new->narg.backquote = copynodelist(n->narg.backquote);
7891 new->narg.text = nodeckstrdup(n->narg.text);
7892 new->narg.next = copynode(n->narg.next);
7893 break;
7894 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00007895#if ENABLE_ASH_BASH_COMPAT
7896 case NTO2:
7897#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007898 case NCLOBBER:
7899 case NFROM:
7900 case NFROMTO:
7901 case NAPPEND:
7902 new->nfile.fname = copynode(n->nfile.fname);
7903 new->nfile.fd = n->nfile.fd;
7904 new->nfile.next = copynode(n->nfile.next);
7905 break;
7906 case NTOFD:
7907 case NFROMFD:
7908 new->ndup.vname = copynode(n->ndup.vname);
7909 new->ndup.dupfd = n->ndup.dupfd;
7910 new->ndup.fd = n->ndup.fd;
7911 new->ndup.next = copynode(n->ndup.next);
7912 break;
7913 case NHERE:
7914 case NXHERE:
7915 new->nhere.doc = copynode(n->nhere.doc);
7916 new->nhere.fd = n->nhere.fd;
7917 new->nhere.next = copynode(n->nhere.next);
7918 break;
7919 case NNOT:
7920 new->nnot.com = copynode(n->nnot.com);
7921 break;
7922 };
7923 new->type = n->type;
7924 return new;
7925}
7926
7927/*
7928 * Make a copy of a parse tree.
7929 */
7930static struct funcnode *
7931copyfunc(union node *n)
7932{
7933 struct funcnode *f;
7934 size_t blocksize;
7935
7936 funcblocksize = offsetof(struct funcnode, n);
7937 funcstringsize = 0;
7938 calcsize(n);
7939 blocksize = funcblocksize;
7940 f = ckmalloc(blocksize + funcstringsize);
7941 funcblock = (char *) f + offsetof(struct funcnode, n);
7942 funcstring = (char *) f + blocksize;
7943 copynode(n);
7944 f->count = 0;
7945 return f;
7946}
7947
7948/*
7949 * Define a shell function.
7950 */
7951static void
7952defun(char *name, union node *func)
7953{
7954 struct cmdentry entry;
7955
7956 INT_OFF;
7957 entry.cmdtype = CMDFUNCTION;
7958 entry.u.func = copyfunc(func);
7959 addcmdentry(name, &entry);
7960 INT_ON;
7961}
7962
Denis Vlasenko4b875702009-03-19 13:30:04 +00007963/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007964#define SKIPBREAK (1 << 0)
7965#define SKIPCONT (1 << 1)
7966#define SKIPFUNC (1 << 2)
7967#define SKIPFILE (1 << 3)
7968#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00007969static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007970static int skipcount; /* number of levels to skip */
7971static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007972static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007973
Denis Vlasenko4b875702009-03-19 13:30:04 +00007974/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007975static int evalstring(char *s, int mask);
7976
Denis Vlasenko4b875702009-03-19 13:30:04 +00007977/* Called to execute a trap.
7978 * Single callsite - at the end of evaltree().
7979 * If we return non-zero, exaltree raises EXEXIT exception.
7980 *
7981 * Perhaps we should avoid entering new trap handlers
7982 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007983 */
7984static int
7985dotrap(void)
7986{
Denis Vlasenko4b875702009-03-19 13:30:04 +00007987 uint8_t *g;
7988 int sig;
7989 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007990
7991 savestatus = exitstatus;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00007992 pendingsig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007993 xbarrier();
7994
Denis Vlasenko653d8e72009-03-19 21:59:35 +00007995 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00007996 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
7997 int want_exexit;
7998 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00007999
Denis Vlasenko4b875702009-03-19 13:30:04 +00008000 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008001 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008002 t = trap[sig];
8003 /* non-trapped SIGINT is handled separately by raise_interrupt,
8004 * don't upset it by resetting gotsig[SIGINT-1] */
8005 if (sig == SIGINT && !t)
8006 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008007
8008 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008009 *g = 0;
8010 if (!t)
8011 continue;
8012 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008013 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008014 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008015 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008016 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008017 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008018 }
8019
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008020 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008021 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008022}
8023
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008024/* forward declarations - evaluation is fairly recursive business... */
Eric Andersenc470f442003-07-28 09:56:35 +00008025static void evalloop(union node *, int);
8026static void evalfor(union node *, int);
8027static void evalcase(union node *, int);
8028static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008029static void expredir(union node *);
Eric Andersenc470f442003-07-28 09:56:35 +00008030static void evalpipe(union node *, int);
8031static void evalcommand(union node *, int);
8032static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008033static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008034
Eric Andersen62483552001-07-10 06:09:16 +00008035/*
Eric Andersenc470f442003-07-28 09:56:35 +00008036 * Evaluate a parse tree. The value is left in the global variable
8037 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008038 */
Eric Andersenc470f442003-07-28 09:56:35 +00008039static void
8040evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008041{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008042 struct jmploc *volatile savehandler = exception_handler;
8043 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008044 int checkexit = 0;
8045 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008046 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008047 int int_level;
8048
8049 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008050
Eric Andersenc470f442003-07-28 09:56:35 +00008051 if (n == NULL) {
8052 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008053 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008054 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008055 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008056
8057 exception_handler = &jmploc;
8058 {
8059 int err = setjmp(jmploc.loc);
8060 if (err) {
8061 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008062 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008063 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8064 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008065 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008066 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008067 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008068 TRACE(("exception %d in evaltree, propagating err=%d\n",
8069 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008070 exception_handler = savehandler;
8071 longjmp(exception_handler->loc, err);
8072 }
8073 }
8074
Eric Andersenc470f442003-07-28 09:56:35 +00008075 switch (n->type) {
8076 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008077#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008078 out1fmt("Node type = %d\n", n->type);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008079 fflush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00008080 break;
8081#endif
8082 case NNOT:
8083 evaltree(n->nnot.com, EV_TESTED);
8084 status = !exitstatus;
8085 goto setstatus;
8086 case NREDIR:
8087 expredir(n->nredir.redirect);
8088 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8089 if (!status) {
8090 evaltree(n->nredir.n, flags & EV_TESTED);
8091 status = exitstatus;
8092 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008093 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008094 goto setstatus;
8095 case NCMD:
8096 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008097 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008098 if (eflag && !(flags & EV_TESTED))
8099 checkexit = ~0;
8100 goto calleval;
8101 case NFOR:
8102 evalfn = evalfor;
8103 goto calleval;
8104 case NWHILE:
8105 case NUNTIL:
8106 evalfn = evalloop;
8107 goto calleval;
8108 case NSUBSHELL:
8109 case NBACKGND:
8110 evalfn = evalsubshell;
8111 goto calleval;
8112 case NPIPE:
8113 evalfn = evalpipe;
8114 goto checkexit;
8115 case NCASE:
8116 evalfn = evalcase;
8117 goto calleval;
8118 case NAND:
8119 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008120 case NSEMI: {
8121
Eric Andersenc470f442003-07-28 09:56:35 +00008122#if NAND + 1 != NOR
8123#error NAND + 1 != NOR
8124#endif
8125#if NOR + 1 != NSEMI
8126#error NOR + 1 != NSEMI
8127#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008128 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008129 evaltree(
8130 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008131 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008132 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008133 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008134 break;
8135 if (!evalskip) {
8136 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008137 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008138 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008139 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008140 evalfn(n, flags);
8141 break;
8142 }
8143 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008144 }
Eric Andersenc470f442003-07-28 09:56:35 +00008145 case NIF:
8146 evaltree(n->nif.test, EV_TESTED);
8147 if (evalskip)
8148 break;
8149 if (exitstatus == 0) {
8150 n = n->nif.ifpart;
8151 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008152 }
8153 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008154 n = n->nif.elsepart;
8155 goto evaln;
8156 }
8157 goto success;
8158 case NDEFUN:
8159 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008160 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008161 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008162 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008163 exitstatus = status;
8164 break;
8165 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008166
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008167 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008168 exception_handler = savehandler;
8169 out1:
8170 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008171 evalskip |= SKIPEVAL;
Denis Vlasenko7c139b42007-03-21 20:17:27 +00008172 else if (pendingsig && dotrap())
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008173 goto exexit;
8174
8175 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008176 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008177 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008178 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008179
8180 RESTORE_INT(int_level);
8181 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008182}
8183
Eric Andersenc470f442003-07-28 09:56:35 +00008184#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8185static
8186#endif
8187void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8188
Eric Andersenc470f442003-07-28 09:56:35 +00008189static void
8190evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008191{
8192 int status;
8193
8194 loopnest++;
8195 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008196 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008197 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008198 int i;
8199
Eric Andersencb57d552001-06-28 07:25:16 +00008200 evaltree(n->nbinary.ch1, EV_TESTED);
8201 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008202 skipping:
8203 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008204 evalskip = 0;
8205 continue;
8206 }
8207 if (evalskip == SKIPBREAK && --skipcount <= 0)
8208 evalskip = 0;
8209 break;
8210 }
Eric Andersenc470f442003-07-28 09:56:35 +00008211 i = exitstatus;
8212 if (n->type != NWHILE)
8213 i = !i;
8214 if (i != 0)
8215 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008216 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008217 status = exitstatus;
8218 if (evalskip)
8219 goto skipping;
8220 }
8221 loopnest--;
8222 exitstatus = status;
8223}
8224
Eric Andersenc470f442003-07-28 09:56:35 +00008225static void
8226evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008227{
8228 struct arglist arglist;
8229 union node *argp;
8230 struct strlist *sp;
8231 struct stackmark smark;
8232
8233 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008234 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008235 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008236 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008237 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008238 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008239 if (evalskip)
8240 goto out;
8241 }
8242 *arglist.lastp = NULL;
8243
8244 exitstatus = 0;
8245 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008246 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008247 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008248 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008249 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008250 if (evalskip) {
8251 if (evalskip == SKIPCONT && --skipcount <= 0) {
8252 evalskip = 0;
8253 continue;
8254 }
8255 if (evalskip == SKIPBREAK && --skipcount <= 0)
8256 evalskip = 0;
8257 break;
8258 }
8259 }
8260 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008261 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008262 popstackmark(&smark);
8263}
8264
Eric Andersenc470f442003-07-28 09:56:35 +00008265static void
8266evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008267{
8268 union node *cp;
8269 union node *patp;
8270 struct arglist arglist;
8271 struct stackmark smark;
8272
8273 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008274 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008275 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008276 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008277 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008278 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8279 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008280 if (casematch(patp, arglist.list->text)) {
8281 if (evalskip == 0) {
8282 evaltree(cp->nclist.body, flags);
8283 }
8284 goto out;
8285 }
8286 }
8287 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008288 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008289 popstackmark(&smark);
8290}
8291
Eric Andersenc470f442003-07-28 09:56:35 +00008292/*
8293 * Kick off a subshell to evaluate a tree.
8294 */
Eric Andersenc470f442003-07-28 09:56:35 +00008295static void
8296evalsubshell(union node *n, int flags)
8297{
8298 struct job *jp;
8299 int backgnd = (n->type == NBACKGND);
8300 int status;
8301
8302 expredir(n->nredir.redirect);
8303 if (!backgnd && flags & EV_EXIT && !trap[0])
8304 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008305 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008306 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008307 if (forkshell(jp, n, backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008308 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008309 flags |= EV_EXIT;
8310 if (backgnd)
8311 flags &=~ EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008312 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008313 redirect(n->nredir.redirect, 0);
8314 evaltreenr(n->nredir.n, flags);
8315 /* never returns */
8316 }
8317 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008318 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008319 status = waitforjob(jp);
8320 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008321 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008322}
8323
Eric Andersenc470f442003-07-28 09:56:35 +00008324/*
8325 * Compute the names of the files in a redirection list.
8326 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008327static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008328static void
8329expredir(union node *n)
8330{
8331 union node *redir;
8332
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008333 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008334 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008335
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008336 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008337 fn.lastp = &fn.list;
8338 switch (redir->type) {
8339 case NFROMTO:
8340 case NFROM:
8341 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008342#if ENABLE_ASH_BASH_COMPAT
8343 case NTO2:
8344#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008345 case NCLOBBER:
8346 case NAPPEND:
8347 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008348#if ENABLE_ASH_BASH_COMPAT
8349 store_expfname:
8350#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008351 redir->nfile.expfname = fn.list->text;
8352 break;
8353 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008354 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008355 if (redir->ndup.vname) {
8356 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008357 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008358 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008359#if ENABLE_ASH_BASH_COMPAT
8360//FIXME: we used expandarg with different args!
8361 if (!isdigit_str9(fn.list->text)) {
8362 /* >&file, not >&fd */
8363 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8364 ash_msg_and_raise_error("redir error");
8365 redir->type = NTO2;
8366 goto store_expfname;
8367 }
8368#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008369 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008370 }
8371 break;
8372 }
8373 }
8374}
8375
Eric Andersencb57d552001-06-28 07:25:16 +00008376/*
Eric Andersencb57d552001-06-28 07:25:16 +00008377 * Evaluate a pipeline. All the processes in the pipeline are children
8378 * of the process creating the pipeline. (This differs from some versions
8379 * of the shell, which make the last process in a pipeline the parent
8380 * of all the rest.)
8381 */
Eric Andersenc470f442003-07-28 09:56:35 +00008382static void
8383evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008384{
8385 struct job *jp;
8386 struct nodelist *lp;
8387 int pipelen;
8388 int prevfd;
8389 int pip[2];
8390
Eric Andersenc470f442003-07-28 09:56:35 +00008391 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008392 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008393 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008394 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008395 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008396 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008397 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008398 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008399 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008400 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008401 pip[1] = -1;
8402 if (lp->next) {
8403 if (pipe(pip) < 0) {
8404 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008405 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008406 }
8407 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008408 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008409 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008410 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008411 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008412 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008413 if (prevfd > 0) {
8414 dup2(prevfd, 0);
8415 close(prevfd);
8416 }
8417 if (pip[1] > 1) {
8418 dup2(pip[1], 1);
8419 close(pip[1]);
8420 }
Eric Andersenc470f442003-07-28 09:56:35 +00008421 evaltreenr(lp->n, flags);
8422 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008423 }
8424 if (prevfd >= 0)
8425 close(prevfd);
8426 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008427 /* Don't want to trigger debugging */
8428 if (pip[1] != -1)
8429 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008430 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008431 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008432 exitstatus = waitforjob(jp);
8433 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008434 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008435 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008436}
8437
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008438/*
8439 * Controls whether the shell is interactive or not.
8440 */
8441static void
8442setinteractive(int on)
8443{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008444 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008445
8446 if (++on == is_interactive)
8447 return;
8448 is_interactive = on;
8449 setsignal(SIGINT);
8450 setsignal(SIGQUIT);
8451 setsignal(SIGTERM);
8452#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8453 if (is_interactive > 1) {
8454 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008455 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008456
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008457 if (!did_banner) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008458 out1fmt(
8459 "\n\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008460 "%s built-in shell (ash)\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008461 "Enter 'help' for a list of built-in commands."
8462 "\n\n",
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008463 bb_banner);
8464 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008465 }
8466 }
8467#endif
8468}
8469
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008470static void
8471optschanged(void)
8472{
8473#if DEBUG
8474 opentrace();
8475#endif
8476 setinteractive(iflag);
8477 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008478#if ENABLE_FEATURE_EDITING_VI
8479 if (viflag)
8480 line_input_state->flags |= VI_MODE;
8481 else
8482 line_input_state->flags &= ~VI_MODE;
8483#else
8484 viflag = 0; /* forcibly keep the option off */
8485#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008486}
8487
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008488static struct localvar *localvars;
8489
8490/*
8491 * Called after a function returns.
8492 * Interrupts must be off.
8493 */
8494static void
8495poplocalvars(void)
8496{
8497 struct localvar *lvp;
8498 struct var *vp;
8499
8500 while ((lvp = localvars) != NULL) {
8501 localvars = lvp->next;
8502 vp = lvp->vp;
8503 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
8504 if (vp == NULL) { /* $- saved */
8505 memcpy(optlist, lvp->text, sizeof(optlist));
8506 free((char*)lvp->text);
8507 optschanged();
8508 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8509 unsetvar(vp->text);
8510 } else {
8511 if (vp->func)
8512 (*vp->func)(strchrnul(lvp->text, '=') + 1);
8513 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
8514 free((char*)vp->text);
8515 vp->flags = lvp->flags;
8516 vp->text = lvp->text;
8517 }
8518 free(lvp);
8519 }
8520}
8521
8522static int
8523evalfun(struct funcnode *func, int argc, char **argv, int flags)
8524{
8525 volatile struct shparam saveparam;
8526 struct localvar *volatile savelocalvars;
8527 struct jmploc *volatile savehandler;
8528 struct jmploc jmploc;
8529 int e;
8530
8531 saveparam = shellparam;
8532 savelocalvars = localvars;
8533 e = setjmp(jmploc.loc);
8534 if (e) {
8535 goto funcdone;
8536 }
8537 INT_OFF;
8538 savehandler = exception_handler;
8539 exception_handler = &jmploc;
8540 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008541 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008542 func->count++;
8543 funcnest++;
8544 INT_ON;
8545 shellparam.nparam = argc - 1;
8546 shellparam.p = argv + 1;
8547#if ENABLE_ASH_GETOPTS
8548 shellparam.optind = 1;
8549 shellparam.optoff = -1;
8550#endif
8551 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008552 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008553 INT_OFF;
8554 funcnest--;
8555 freefunc(func);
8556 poplocalvars();
8557 localvars = savelocalvars;
8558 freeparam(&shellparam);
8559 shellparam = saveparam;
8560 exception_handler = savehandler;
8561 INT_ON;
8562 evalskip &= ~SKIPFUNC;
8563 return e;
8564}
8565
Denis Vlasenko131ae172007-02-18 13:00:19 +00008566#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008567static char **
8568parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008569{
8570 char *cp, c;
8571
8572 for (;;) {
8573 cp = *++argv;
8574 if (!cp)
8575 return 0;
8576 if (*cp++ != '-')
8577 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008578 c = *cp++;
8579 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008580 break;
8581 if (c == '-' && !*cp) {
8582 argv++;
8583 break;
8584 }
8585 do {
8586 switch (c) {
8587 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008588 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008589 break;
8590 default:
8591 /* run 'typecmd' for other options */
8592 return 0;
8593 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008594 c = *cp++;
8595 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008596 }
8597 return argv;
8598}
8599#endif
8600
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008601/*
8602 * Make a variable a local variable. When a variable is made local, it's
8603 * value and flags are saved in a localvar structure. The saved values
8604 * will be restored when the shell function returns. We handle the name
8605 * "-" as a special case.
8606 */
8607static void
8608mklocal(char *name)
8609{
8610 struct localvar *lvp;
8611 struct var **vpp;
8612 struct var *vp;
8613
8614 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008615 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008616 if (LONE_DASH(name)) {
8617 char *p;
8618 p = ckmalloc(sizeof(optlist));
8619 lvp->text = memcpy(p, optlist, sizeof(optlist));
8620 vp = NULL;
8621 } else {
8622 char *eq;
8623
8624 vpp = hashvar(name);
8625 vp = *findvar(vpp, name);
8626 eq = strchr(name, '=');
8627 if (vp == NULL) {
8628 if (eq)
8629 setvareq(name, VSTRFIXED);
8630 else
8631 setvar(name, NULL, VSTRFIXED);
8632 vp = *vpp; /* the new variable */
8633 lvp->flags = VUNSET;
8634 } else {
8635 lvp->text = vp->text;
8636 lvp->flags = vp->flags;
8637 vp->flags |= VSTRFIXED|VTEXTFIXED;
8638 if (eq)
8639 setvareq(name, 0);
8640 }
8641 }
8642 lvp->vp = vp;
8643 lvp->next = localvars;
8644 localvars = lvp;
8645 INT_ON;
8646}
8647
8648/*
8649 * The "local" command.
8650 */
8651static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008652localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008653{
8654 char *name;
8655
8656 argv = argptr;
8657 while ((name = *argv++) != NULL) {
8658 mklocal(name);
8659 }
8660 return 0;
8661}
8662
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008663static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008664falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008665{
8666 return 1;
8667}
8668
8669static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008670truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008671{
8672 return 0;
8673}
8674
8675static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008676execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008677{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008678 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008679 iflag = 0; /* exit on error */
8680 mflag = 0;
8681 optschanged();
8682 shellexec(argv + 1, pathval(), 0);
8683 }
8684 return 0;
8685}
8686
8687/*
8688 * The return command.
8689 */
8690static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008691returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008692{
8693 /*
8694 * If called outside a function, do what ksh does;
8695 * skip the rest of the file.
8696 */
8697 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8698 return argv[1] ? number(argv[1]) : exitstatus;
8699}
8700
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008701/* Forward declarations for builtintab[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008702static int breakcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008703static int dotcmd(int, char **);
8704static int evalcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008705static int exitcmd(int, char **);
8706static int exportcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008707#if ENABLE_ASH_GETOPTS
8708static int getoptscmd(int, char **);
8709#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008710#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008711static int helpcmd(int, char **);
Denis Vlasenko52764022007-02-24 13:42:56 +00008712#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008713#if ENABLE_ASH_MATH_SUPPORT
8714static int letcmd(int, char **);
8715#endif
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008716static int readcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008717static int setcmd(int, char **);
8718static int shiftcmd(int, char **);
8719static int timescmd(int, char **);
8720static int trapcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008721static int umaskcmd(int, char **);
8722static int unsetcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008723static int ulimitcmd(int, char **);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008724
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008725#define BUILTIN_NOSPEC "0"
8726#define BUILTIN_SPECIAL "1"
8727#define BUILTIN_REGULAR "2"
8728#define BUILTIN_SPEC_REG "3"
8729#define BUILTIN_ASSIGN "4"
8730#define BUILTIN_SPEC_ASSG "5"
8731#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008732#define BUILTIN_SPEC_REG_ASSG "7"
8733
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008734/* We do not handle [[ expr ]] bashism bash-compatibly,
8735 * we make it a synonym of [ expr ].
8736 * Basically, word splitting and pathname expansion should NOT be performed
8737 * Examples:
8738 * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
8739 * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
8740 * Additional operators:
8741 * || and && should work as -o and -a
8742 * =~ regexp match
8743 * Apart from the above, [[ expr ]] should work as [ expr ]
8744 */
8745
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008746#define echocmd echo_main
8747#define printfcmd printf_main
8748#define testcmd test_main
Denis Vlasenko468aea22008-04-01 14:47:57 +00008749
Denis Vlasenkof7d56652008-03-25 05:51:41 +00008750/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008751static const struct builtincmd builtintab[] = {
8752 { BUILTIN_SPEC_REG ".", dotcmd },
8753 { BUILTIN_SPEC_REG ":", truecmd },
8754#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008755 { BUILTIN_REGULAR "[", testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00008756#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008757 { BUILTIN_REGULAR "[[", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008758#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00008759#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008760#if ENABLE_ASH_ALIAS
8761 { BUILTIN_REG_ASSG "alias", aliascmd },
8762#endif
8763#if JOBS
8764 { BUILTIN_REGULAR "bg", fg_bgcmd },
8765#endif
8766 { BUILTIN_SPEC_REG "break", breakcmd },
8767 { BUILTIN_REGULAR "cd", cdcmd },
8768 { BUILTIN_NOSPEC "chdir", cdcmd },
8769#if ENABLE_ASH_CMDCMD
8770 { BUILTIN_REGULAR "command", commandcmd },
8771#endif
8772 { BUILTIN_SPEC_REG "continue", breakcmd },
8773#if ENABLE_ASH_BUILTIN_ECHO
8774 { BUILTIN_REGULAR "echo", echocmd },
8775#endif
8776 { BUILTIN_SPEC_REG "eval", evalcmd },
8777 { BUILTIN_SPEC_REG "exec", execcmd },
8778 { BUILTIN_SPEC_REG "exit", exitcmd },
8779 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
8780 { BUILTIN_REGULAR "false", falsecmd },
8781#if JOBS
8782 { BUILTIN_REGULAR "fg", fg_bgcmd },
8783#endif
8784#if ENABLE_ASH_GETOPTS
8785 { BUILTIN_REGULAR "getopts", getoptscmd },
8786#endif
8787 { BUILTIN_NOSPEC "hash", hashcmd },
8788#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8789 { BUILTIN_NOSPEC "help", helpcmd },
8790#endif
8791#if JOBS
8792 { BUILTIN_REGULAR "jobs", jobscmd },
8793 { BUILTIN_REGULAR "kill", killcmd },
8794#endif
8795#if ENABLE_ASH_MATH_SUPPORT
8796 { BUILTIN_NOSPEC "let", letcmd },
8797#endif
8798 { BUILTIN_ASSIGN "local", localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008799#if ENABLE_ASH_BUILTIN_PRINTF
8800 { BUILTIN_REGULAR "printf", printfcmd },
8801#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008802 { BUILTIN_NOSPEC "pwd", pwdcmd },
8803 { BUILTIN_REGULAR "read", readcmd },
8804 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
8805 { BUILTIN_SPEC_REG "return", returncmd },
8806 { BUILTIN_SPEC_REG "set", setcmd },
8807 { BUILTIN_SPEC_REG "shift", shiftcmd },
8808 { BUILTIN_SPEC_REG "source", dotcmd },
8809#if ENABLE_ASH_BUILTIN_TEST
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00008810 { BUILTIN_REGULAR "test", testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008811#endif
8812 { BUILTIN_SPEC_REG "times", timescmd },
8813 { BUILTIN_SPEC_REG "trap", trapcmd },
8814 { BUILTIN_REGULAR "true", truecmd },
8815 { BUILTIN_NOSPEC "type", typecmd },
8816 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
8817 { BUILTIN_REGULAR "umask", umaskcmd },
8818#if ENABLE_ASH_ALIAS
8819 { BUILTIN_REGULAR "unalias", unaliascmd },
8820#endif
8821 { BUILTIN_SPEC_REG "unset", unsetcmd },
8822 { BUILTIN_REGULAR "wait", waitcmd },
8823};
8824
Denis Vlasenko80591b02008-03-25 07:49:43 +00008825/* Should match the above table! */
8826#define COMMANDCMD (builtintab + \
8827 2 + \
8828 1 * ENABLE_ASH_BUILTIN_TEST + \
8829 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8830 1 * ENABLE_ASH_ALIAS + \
8831 1 * ENABLE_ASH_JOB_CONTROL + \
8832 3)
8833#define EXECCMD (builtintab + \
8834 2 + \
8835 1 * ENABLE_ASH_BUILTIN_TEST + \
8836 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
8837 1 * ENABLE_ASH_ALIAS + \
8838 1 * ENABLE_ASH_JOB_CONTROL + \
8839 3 + \
8840 1 * ENABLE_ASH_CMDCMD + \
8841 1 + \
8842 ENABLE_ASH_BUILTIN_ECHO + \
8843 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008844
8845/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008846 * Search the table of builtin commands.
8847 */
8848static struct builtincmd *
8849find_builtin(const char *name)
8850{
8851 struct builtincmd *bp;
8852
8853 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008854 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008855 pstrcmp
8856 );
8857 return bp;
8858}
8859
8860/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008861 * Execute a simple command.
8862 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008863static int
8864isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00008865{
8866 const char *q = endofname(p);
8867 if (p == q)
8868 return 0;
8869 return *q == '=';
8870}
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008871static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008872bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008873{
8874 /* Preserve exitstatus of a previous possible redirection
8875 * as POSIX mandates */
8876 return back_exitstatus;
8877}
Eric Andersenc470f442003-07-28 09:56:35 +00008878static void
8879evalcommand(union node *cmd, int flags)
8880{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008881 static const struct builtincmd null_bltin = {
8882 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008883 };
Eric Andersenc470f442003-07-28 09:56:35 +00008884 struct stackmark smark;
8885 union node *argp;
8886 struct arglist arglist;
8887 struct arglist varlist;
8888 char **argv;
8889 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008890 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00008891 struct cmdentry cmdentry;
8892 struct job *jp;
8893 char *lastarg;
8894 const char *path;
8895 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008896 int status;
8897 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00008898 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008899 smallint cmd_is_exec;
8900 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00008901
8902 /* First expand the arguments. */
8903 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
8904 setstackmark(&smark);
8905 back_exitstatus = 0;
8906
8907 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008908 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00008909 varlist.lastp = &varlist.list;
8910 *varlist.lastp = NULL;
8911 arglist.lastp = &arglist.list;
8912 *arglist.lastp = NULL;
8913
8914 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008915 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00008916 bcmd = find_builtin(cmd->ncmd.args->narg.text);
8917 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
8918 }
8919
Eric Andersenc470f442003-07-28 09:56:35 +00008920 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
8921 struct strlist **spp;
8922
8923 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00008924 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00008925 expandarg(argp, &arglist, EXP_VARTILDE);
8926 else
8927 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
8928
Eric Andersenc470f442003-07-28 09:56:35 +00008929 for (sp = *spp; sp; sp = sp->next)
8930 argc++;
8931 }
8932
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008933 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008934 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008935 TRACE(("evalcommand arg: %s\n", sp->text));
8936 *nargv++ = sp->text;
8937 }
8938 *nargv = NULL;
8939
8940 lastarg = NULL;
8941 if (iflag && funcnest == 0 && argc > 0)
8942 lastarg = nargv[-1];
8943
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008944 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00008945 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008946 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00008947
8948 path = vpath.text;
8949 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
8950 struct strlist **spp;
8951 char *p;
8952
8953 spp = varlist.lastp;
8954 expandarg(argp, &varlist, EXP_VARTILDE);
8955
8956 /*
8957 * Modify the command lookup path, if a PATH= assignment
8958 * is present
8959 */
8960 p = (*spp)->text;
8961 if (varequal(p, path))
8962 path = p;
8963 }
8964
8965 /* Print the command if xflag is set. */
8966 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008967 int n;
8968 const char *p = " %s";
Eric Andersenc470f442003-07-28 09:56:35 +00008969
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008970 p++;
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008971 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008972
8973 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008974 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008975 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00008976 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008977 sp = sp->next;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00008978 if (*p == '%') {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00008979 p--;
8980 }
8981 }
8982 sp = arglist.list;
8983 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00008984 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008985 }
8986
8987 cmd_is_exec = 0;
8988 spclbltin = -1;
8989
8990 /* Now locate the command. */
8991 if (argc) {
8992 const char *oldpath;
8993 int cmd_flag = DO_ERR;
8994
8995 path += 5;
8996 oldpath = path;
8997 for (;;) {
8998 find_command(argv[0], &cmdentry, cmd_flag, path);
8999 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00009000 flush_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009001 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009002 goto bail;
9003 }
9004
9005 /* implement bltin and command here */
9006 if (cmdentry.cmdtype != CMDBUILTIN)
9007 break;
9008 if (spclbltin < 0)
9009 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9010 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009011 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009012#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009013 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009014 path = oldpath;
9015 nargv = parse_command_args(argv, &path);
9016 if (!nargv)
9017 break;
9018 argc -= nargv - argv;
9019 argv = nargv;
9020 cmd_flag |= DO_NOFUNC;
9021 } else
9022#endif
9023 break;
9024 }
9025 }
9026
9027 if (status) {
9028 /* We have a redirection error. */
9029 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009030 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009031 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009032 exitstatus = status;
9033 goto out;
9034 }
9035
9036 /* Execute the command. */
9037 switch (cmdentry.cmdtype) {
9038 default:
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009039
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009040#if ENABLE_FEATURE_SH_NOFORK
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009041/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
9042 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009043 {
9044 /* find_command() encodes applet_no as (-2 - applet_no) */
9045 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009046 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009047 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009048 /* run <applet>_main() */
9049 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009050 break;
9051 }
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009052 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009053#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009054 /* Fork off a child process if necessary. */
9055 if (!(flags & EV_EXIT) || trap[0]) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00009056 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009057 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009058 if (forkshell(jp, cmd, FORK_FG) != 0) {
9059 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009060 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009061 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009062 break;
9063 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009064 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009065 }
9066 listsetvar(varlist.list, VEXPORT|VSTACK);
9067 shellexec(argv, path, cmdentry.u.index);
9068 /* NOTREACHED */
9069
9070 case CMDBUILTIN:
9071 cmdenviron = varlist.list;
9072 if (cmdenviron) {
9073 struct strlist *list = cmdenviron;
9074 int i = VNOSET;
9075 if (spclbltin > 0 || argc == 0) {
9076 i = 0;
9077 if (cmd_is_exec && argc > 1)
9078 i = VEXPORT;
9079 }
9080 listsetvar(list, i);
9081 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009082 /* Tight loop with builtins only:
9083 * "while kill -0 $child; do true; done"
9084 * will never exit even if $child died, unless we do this
9085 * to reap the zombie and make kill detect that it's gone: */
9086 dowait(DOWAIT_NONBLOCK, NULL);
9087
Eric Andersenc470f442003-07-28 09:56:35 +00009088 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9089 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009090 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009091 if (i == EXEXIT)
9092 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009093 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009094 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009095 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009096 if (i == EXSIG)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009097 exit_status = 128 + pendingsig;
Eric Andersenc470f442003-07-28 09:56:35 +00009098 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009099 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009100 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009101 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009102 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009103 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009104 }
9105 break;
9106
9107 case CMDFUNCTION:
9108 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009109 /* See above for the rationale */
9110 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009111 if (evalfun(cmdentry.u.func, argc, argv, flags))
9112 goto raise;
9113 break;
9114 }
9115
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009116 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009117 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009118 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009119 /* dsl: I think this is intended to be used to support
9120 * '_' in 'vi' command mode during line editing...
9121 * However I implemented that within libedit itself.
9122 */
9123 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009124 }
Eric Andersenc470f442003-07-28 09:56:35 +00009125 popstackmark(&smark);
9126}
9127
9128static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009129evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9130{
Eric Andersenc470f442003-07-28 09:56:35 +00009131 char *volatile savecmdname;
9132 struct jmploc *volatile savehandler;
9133 struct jmploc jmploc;
9134 int i;
9135
9136 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009137 i = setjmp(jmploc.loc);
9138 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009139 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009140 savehandler = exception_handler;
9141 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009142 commandname = argv[0];
9143 argptr = argv + 1;
9144 optptr = NULL; /* initialize nextopt */
9145 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009146 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009147 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009148 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009149 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009150 commandname = savecmdname;
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009151// exsig = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009152 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009153
9154 return i;
9155}
9156
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009157static int
9158goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009159{
9160 return !*endofname(p);
9161}
9162
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009163
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009164/*
9165 * Search for a command. This is called before we fork so that the
9166 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009167 * the child. The check for "goodname" is an overly conservative
9168 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009169 */
Eric Andersenc470f442003-07-28 09:56:35 +00009170static void
9171prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009172{
9173 struct cmdentry entry;
9174
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009175 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9176 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009177}
9178
Eric Andersencb57d552001-06-28 07:25:16 +00009179
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009180/* ============ Builtin commands
9181 *
9182 * Builtin commands whose functions are closely tied to evaluation
9183 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009184 */
9185
9186/*
Eric Andersencb57d552001-06-28 07:25:16 +00009187 * Handle break and continue commands. Break, continue, and return are
9188 * all handled by setting the evalskip flag. The evaluation routines
9189 * above all check this flag, and if it is set they start skipping
9190 * commands rather than executing them. The variable skipcount is
9191 * the number of loops to break/continue, or the number of function
9192 * levels to return. (The latter is always 1.) It should probably
9193 * be an error to break out of more loops than exist, but it isn't
9194 * in the standard shell so we don't make it one here.
9195 */
Eric Andersenc470f442003-07-28 09:56:35 +00009196static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009197breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009198{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009199 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009200
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009201 if (n <= 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009202 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009203 if (n > loopnest)
9204 n = loopnest;
9205 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009206 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009207 skipcount = n;
9208 }
9209 return 0;
9210}
9211
Eric Andersenc470f442003-07-28 09:56:35 +00009212
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009213/* ============ input.c
9214 *
Eric Andersen90898442003-08-06 11:20:52 +00009215 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009216 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009217
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009218enum {
9219 INPUT_PUSH_FILE = 1,
9220 INPUT_NOFILE_OK = 2,
9221};
Eric Andersencb57d552001-06-28 07:25:16 +00009222
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009223static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009224/* values of checkkwd variable */
9225#define CHKALIAS 0x1
9226#define CHKKWD 0x2
9227#define CHKNL 0x4
9228
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009229/*
9230 * Push a string back onto the input at this current parsefile level.
9231 * We handle aliases this way.
9232 */
9233#if !ENABLE_ASH_ALIAS
9234#define pushstring(s, ap) pushstring(s)
9235#endif
9236static void
9237pushstring(char *s, struct alias *ap)
9238{
9239 struct strpush *sp;
9240 int len;
9241
9242 len = strlen(s);
9243 INT_OFF;
9244 if (g_parsefile->strpush) {
9245 sp = ckzalloc(sizeof(*sp));
9246 sp->prev = g_parsefile->strpush;
9247 } else {
9248 sp = &(g_parsefile->basestrpush);
9249 }
9250 g_parsefile->strpush = sp;
9251 sp->prev_string = g_parsefile->next_to_pgetc;
9252 sp->prev_left_in_line = g_parsefile->left_in_line;
9253#if ENABLE_ASH_ALIAS
9254 sp->ap = ap;
9255 if (ap) {
9256 ap->flag |= ALIASINUSE;
9257 sp->string = s;
9258 }
9259#endif
9260 g_parsefile->next_to_pgetc = s;
9261 g_parsefile->left_in_line = len;
9262 INT_ON;
9263}
9264
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009265static void
9266popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009267{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009268 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009269
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009270 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009271#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009272 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009273 if (g_parsefile->next_to_pgetc[-1] == ' '
9274 || g_parsefile->next_to_pgetc[-1] == '\t'
9275 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009276 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009277 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009278 if (sp->string != sp->ap->val) {
9279 free(sp->string);
9280 }
9281 sp->ap->flag &= ~ALIASINUSE;
9282 if (sp->ap->flag & ALIASDEAD) {
9283 unalias(sp->ap->name);
9284 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009285 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009286#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009287 g_parsefile->next_to_pgetc = sp->prev_string;
9288 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009289 g_parsefile->strpush = sp->prev;
9290 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009291 free(sp);
9292 INT_ON;
9293}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009294
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009295//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9296//it peeks whether it is &>, and then pushes back both chars.
9297//This function needs to save last *next_to_pgetc to buf[0]
9298//to make two pungetc() reliable. Currently,
9299// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009300static int
9301preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009302{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009303 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009304 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009305
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009306 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009307#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009308 retry:
Denis Vlasenko727752d2008-11-28 03:41:47 +00009309 if (!iflag || g_parsefile->fd != STDIN_FILENO)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009310 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009311 else {
Denis Vlasenko38f63192007-01-22 09:03:07 +00009312#if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009313 line_input_state->path_lookup = pathval();
Eric Andersen4a79c0e2004-09-08 10:01:07 +00009314#endif
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009315 nr = read_line_input(cmdedit_prompt, buf, BUFSIZ, line_input_state);
9316 if (nr == 0) {
9317 /* Ctrl+C pressed */
9318 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009319 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009320 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009321 raise(SIGINT);
9322 return 1;
9323 }
Eric Andersenc470f442003-07-28 09:56:35 +00009324 goto retry;
9325 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009326 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009327 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009328 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009329 }
Eric Andersencb57d552001-06-28 07:25:16 +00009330 }
9331#else
Denis Vlasenkocc3f20b2008-06-23 22:31:52 +00009332 nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009333#endif
9334
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009335#if 0
9336/* nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009337 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009338 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009339 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009340 if (flags >= 0 && (flags & O_NONBLOCK)) {
9341 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009342 if (fcntl(0, F_SETFL, flags) >= 0) {
9343 out2str("sh: turning off NDELAY mode\n");
9344 goto retry;
9345 }
9346 }
9347 }
9348 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009349#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009350 return nr;
9351}
9352
9353/*
9354 * Refill the input buffer and return the next input character:
9355 *
9356 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009357 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9358 * or we are reading from a string so we can't refill the buffer,
9359 * return EOF.
Eric Andersencb57d552001-06-28 07:25:16 +00009360 * 3) If the is more stuff in this buffer, use it else call read to fill it.
9361 * 4) Process input up to the next newline, deleting nul characters.
9362 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009363//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9364#define pgetc_debug(...) ((void)0)
Denis Vlasenko68819d12008-12-15 11:26:36 +00009365/*
9366 * NB: due to SIT(c) internals (syntax_index_table[] vector),
9367 * pgetc() and related functions must return chars SIGN-EXTENDED into ints,
9368 * not zero-extended. Seems fragile to me. Affects only !USE_SIT_FUNCTION case,
9369 * so we can fix it by ditching !USE_SIT_FUNCTION if Unicode requires that.
9370 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009371static int
Eric Andersenc470f442003-07-28 09:56:35 +00009372preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009373{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009374 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009375 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009376
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009377 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009378#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009379 if (g_parsefile->left_in_line == -1
9380 && g_parsefile->strpush->ap
9381 && g_parsefile->next_to_pgetc[-1] != ' '
9382 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009383 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009384 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009385 return PEOA;
9386 }
Eric Andersen2870d962001-07-02 17:27:21 +00009387#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009388 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009389 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009390 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9391 g_parsefile->left_in_line,
9392 g_parsefile->next_to_pgetc,
9393 g_parsefile->next_to_pgetc);
9394 if (--g_parsefile->left_in_line >= 0)
9395 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009396 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009397 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009398 * "pgetc" needs refilling.
9399 */
9400
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009401 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009402 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009403 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009404 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009405 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009406 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009407 /* even in failure keep left_in_line and next_to_pgetc
9408 * in lock step, for correct multi-layer pungetc.
9409 * left_in_line was decremented before preadbuffer(),
9410 * must inc next_to_pgetc: */
9411 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009412 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009413 }
Eric Andersencb57d552001-06-28 07:25:16 +00009414
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009415 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009416 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009417 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009418 again:
9419 more = preadfd();
9420 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009421 /* don't try reading again */
9422 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009423 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009424 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009425 return PEOF;
9426 }
9427 }
9428
Denis Vlasenko727752d2008-11-28 03:41:47 +00009429 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009430 * Set g_parsefile->left_in_line
9431 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009432 * NUL chars are deleted.
9433 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009434 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009435 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009436 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009437
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009438 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009439
Denis Vlasenko727752d2008-11-28 03:41:47 +00009440 c = *q;
9441 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009442 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009443 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009444 q++;
9445 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009446 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009447 break;
9448 }
Eric Andersencb57d552001-06-28 07:25:16 +00009449 }
9450
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009451 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009452 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9453 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009454 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009455 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009456 }
9457 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009458 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009459
Eric Andersencb57d552001-06-28 07:25:16 +00009460 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009461 char save = *q;
9462 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009463 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009464 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009465 }
9466
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009467 pgetc_debug("preadbuffer at %d:%p'%s'",
9468 g_parsefile->left_in_line,
9469 g_parsefile->next_to_pgetc,
9470 g_parsefile->next_to_pgetc);
Denis Vlasenko68819d12008-12-15 11:26:36 +00009471 return signed_char2int(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009472}
9473
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009474#define pgetc_as_macro() \
9475 (--g_parsefile->left_in_line >= 0 \
Denis Vlasenko68819d12008-12-15 11:26:36 +00009476 ? signed_char2int(*g_parsefile->next_to_pgetc++) \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009477 : preadbuffer() \
9478 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009479
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009480static int
9481pgetc(void)
9482{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009483 pgetc_debug("pgetc_fast at %d:%p'%s'",
9484 g_parsefile->left_in_line,
9485 g_parsefile->next_to_pgetc,
9486 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009487 return pgetc_as_macro();
9488}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009489
9490#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denis Vlasenko834dee72008-10-07 09:18:30 +00009491#define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009492#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009493#define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009494#endif
9495
9496/*
9497 * Same as pgetc(), but ignores PEOA.
9498 */
9499#if ENABLE_ASH_ALIAS
9500static int
9501pgetc2(void)
9502{
9503 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009504 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009505 pgetc_debug("pgetc_fast at %d:%p'%s'",
9506 g_parsefile->left_in_line,
9507 g_parsefile->next_to_pgetc,
9508 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009509 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009510 } while (c == PEOA);
9511 return c;
9512}
9513#else
Denis Vlasenko834dee72008-10-07 09:18:30 +00009514#define pgetc2() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009515#endif
9516
9517/*
9518 * Read a line from the script.
9519 */
9520static char *
9521pfgets(char *line, int len)
9522{
9523 char *p = line;
9524 int nleft = len;
9525 int c;
9526
9527 while (--nleft > 0) {
9528 c = pgetc2();
9529 if (c == PEOF) {
9530 if (p == line)
9531 return NULL;
9532 break;
9533 }
9534 *p++ = c;
9535 if (c == '\n')
9536 break;
9537 }
9538 *p = '\0';
9539 return line;
9540}
9541
Eric Andersenc470f442003-07-28 09:56:35 +00009542/*
9543 * Undo the last call to pgetc. Only one character may be pushed back.
9544 * PEOF may be pushed back.
9545 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009546static void
Eric Andersenc470f442003-07-28 09:56:35 +00009547pungetc(void)
9548{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009549 g_parsefile->left_in_line++;
9550 g_parsefile->next_to_pgetc--;
9551 pgetc_debug("pushed back to %d:%p'%s'",
9552 g_parsefile->left_in_line,
9553 g_parsefile->next_to_pgetc,
9554 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009555}
9556
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009557/*
9558 * To handle the "." command, a stack of input files is used. Pushfile
9559 * adds a new entry to the stack and popfile restores the previous level.
9560 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009561static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009562pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009563{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009564 struct parsefile *pf;
9565
Denis Vlasenko597906c2008-02-20 16:38:54 +00009566 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009567 pf->prev = g_parsefile;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009568 pf->fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009569 /*pf->strpush = NULL; - ckzalloc did it */
9570 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009571 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009572}
9573
9574static void
9575popfile(void)
9576{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009577 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009578
Denis Vlasenkob012b102007-02-19 22:43:01 +00009579 INT_OFF;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009580 if (pf->fd >= 0)
9581 close(pf->fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009582 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009583 while (pf->strpush)
9584 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009585 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009586 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009587 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009588}
9589
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009590/*
9591 * Return to top level.
9592 */
9593static void
9594popallfiles(void)
9595{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009596 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009597 popfile();
9598}
9599
9600/*
9601 * Close the file(s) that the shell is reading commands from. Called
9602 * after a fork is done.
9603 */
9604static void
9605closescript(void)
9606{
9607 popallfiles();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009608 if (g_parsefile->fd > 0) {
9609 close(g_parsefile->fd);
9610 g_parsefile->fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009611 }
9612}
9613
9614/*
9615 * Like setinputfile, but takes an open file descriptor. Call this with
9616 * interrupts off.
9617 */
9618static void
9619setinputfd(int fd, int push)
9620{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009621 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009622 if (push) {
9623 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009624 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009625 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009626 g_parsefile->fd = fd;
9627 if (g_parsefile->buf == NULL)
9628 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009629 g_parsefile->left_in_buffer = 0;
9630 g_parsefile->left_in_line = 0;
9631 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009632}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009633
Eric Andersenc470f442003-07-28 09:56:35 +00009634/*
9635 * Set the input to take input from a file. If push is set, push the
9636 * old input onto the stack first.
9637 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009638static int
9639setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009640{
9641 int fd;
9642 int fd2;
9643
Denis Vlasenkob012b102007-02-19 22:43:01 +00009644 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009645 fd = open(fname, O_RDONLY);
9646 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009647 if (flags & INPUT_NOFILE_OK)
9648 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009649 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009650 }
Eric Andersenc470f442003-07-28 09:56:35 +00009651 if (fd < 10) {
9652 fd2 = copyfd(fd, 10);
9653 close(fd);
9654 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009655 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009656 fd = fd2;
9657 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009658 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009659 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009660 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009661 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009662}
9663
Eric Andersencb57d552001-06-28 07:25:16 +00009664/*
9665 * Like setinputfile, but takes input from a string.
9666 */
Eric Andersenc470f442003-07-28 09:56:35 +00009667static void
9668setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009669{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009670 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009671 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009672 g_parsefile->next_to_pgetc = string;
9673 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009674 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009675 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009676 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009677}
9678
9679
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009680/* ============ mail.c
9681 *
9682 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009683 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009684
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009685#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009686
Eric Andersencb57d552001-06-28 07:25:16 +00009687#define MAXMBOXES 10
9688
Eric Andersenc470f442003-07-28 09:56:35 +00009689/* times of mailboxes */
9690static time_t mailtime[MAXMBOXES];
9691/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009692static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009693
Eric Andersencb57d552001-06-28 07:25:16 +00009694/*
Eric Andersenc470f442003-07-28 09:56:35 +00009695 * Print appropriate message(s) if mail has arrived.
9696 * If mail_var_path_changed is set,
9697 * then the value of MAIL has mail_var_path_changed,
9698 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009699 */
Eric Andersenc470f442003-07-28 09:56:35 +00009700static void
9701chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009702{
Eric Andersencb57d552001-06-28 07:25:16 +00009703 const char *mpath;
9704 char *p;
9705 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009706 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009707 struct stackmark smark;
9708 struct stat statb;
9709
Eric Andersencb57d552001-06-28 07:25:16 +00009710 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009711 mpath = mpathset() ? mpathval() : mailval();
9712 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Eric Andersencb57d552001-06-28 07:25:16 +00009713 p = padvance(&mpath, nullstr);
9714 if (p == NULL)
9715 break;
9716 if (*p == '\0')
9717 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009718 for (q = p; *q; q++)
9719 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009720#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009721 if (q[-1] != '/')
9722 abort();
9723#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009724 q[-1] = '\0'; /* delete trailing '/' */
9725 if (stat(p, &statb) < 0) {
9726 *mtp = 0;
9727 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009728 }
Eric Andersenc470f442003-07-28 09:56:35 +00009729 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9730 fprintf(
9731 stderr, snlfmt,
9732 pathopt ? pathopt : "you have mail"
9733 );
9734 }
9735 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009736 }
Eric Andersenc470f442003-07-28 09:56:35 +00009737 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009738 popstackmark(&smark);
9739}
Eric Andersencb57d552001-06-28 07:25:16 +00009740
Eric Andersenc470f442003-07-28 09:56:35 +00009741static void
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009742changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009743{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009744 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009745}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009746
Denis Vlasenko131ae172007-02-18 13:00:19 +00009747#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009748
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009749
9750/* ============ ??? */
9751
Eric Andersencb57d552001-06-28 07:25:16 +00009752/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009753 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +00009754 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009755static void
9756setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009757{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009758 char **newparam;
9759 char **ap;
9760 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +00009761
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009762 for (nparam = 0; argv[nparam]; nparam++)
9763 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009764 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
9765 while (*argv) {
9766 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +00009767 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009768 *ap = NULL;
9769 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +00009770 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009771 shellparam.nparam = nparam;
9772 shellparam.p = newparam;
9773#if ENABLE_ASH_GETOPTS
9774 shellparam.optind = 1;
9775 shellparam.optoff = -1;
9776#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009777}
9778
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009779/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009780 * Process shell options. The global variable argptr contains a pointer
9781 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009782 *
9783 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
9784 * For a non-interactive shell, an error condition encountered
9785 * by a special built-in ... shall cause the shell to write a diagnostic message
9786 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +00009787 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +00009788 * ...
9789 * Utility syntax error (option or operand error) Shall exit
9790 * ...
9791 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
9792 * we see that bash does not do that (set "finishes" with error code 1 instead,
9793 * and shell continues), and people rely on this behavior!
9794 * Testcase:
9795 * set -o barfoo 2>/dev/null
9796 * echo $?
9797 *
9798 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009799 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009800static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009801plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +00009802{
9803 int i;
9804
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009805 if (name) {
9806 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009807 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00009808 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009809 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009810 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +00009811 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009812 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009813 return 1;
Eric Andersen62483552001-07-10 06:09:16 +00009814 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009815 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009816 if (val) {
9817 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
9818 } else {
9819 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
9820 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00009821 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009822 return 0;
Eric Andersen62483552001-07-10 06:09:16 +00009823}
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009824static void
9825setoption(int flag, int val)
9826{
9827 int i;
9828
9829 for (i = 0; i < NOPTS; i++) {
9830 if (optletters(i) == flag) {
9831 optlist[i] = val;
9832 return;
9833 }
9834 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009835 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009836 /* NOTREACHED */
9837}
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009838static int
Eric Andersenc470f442003-07-28 09:56:35 +00009839options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +00009840{
9841 char *p;
9842 int val;
9843 int c;
9844
9845 if (cmdline)
9846 minusc = NULL;
9847 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009848 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009849 if (c != '-' && c != '+')
9850 break;
9851 argptr++;
9852 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009853 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +00009854 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +00009855 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +00009856 if (!cmdline) {
9857 /* "-" means turn off -x and -v */
9858 if (p[0] == '\0')
9859 xflag = vflag = 0;
9860 /* "--" means reset params */
9861 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +00009862 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +00009863 }
Eric Andersenc470f442003-07-28 09:56:35 +00009864 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +00009865 }
Eric Andersencb57d552001-06-28 07:25:16 +00009866 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009867 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +00009868 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009869 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +00009870 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009871 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +00009872 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +00009873 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009874 /* it already printed err message */
9875 return 1; /* error */
9876 }
Eric Andersencb57d552001-06-28 07:25:16 +00009877 if (*argptr)
9878 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +00009879 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
9880 isloginsh = 1;
9881 /* bash does not accept +-login, we also won't */
9882 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009883 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +00009884 isloginsh = 1;
9885 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009886 } else {
9887 setoption(c, val);
9888 }
9889 }
9890 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009891 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009892}
9893
Eric Andersencb57d552001-06-28 07:25:16 +00009894/*
Eric Andersencb57d552001-06-28 07:25:16 +00009895 * The shift builtin command.
9896 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009897static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009898shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009899{
9900 int n;
9901 char **ap1, **ap2;
9902
9903 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009904 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +00009905 n = number(argv[1]);
9906 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +00009907 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009908 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009909 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009910 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +00009911 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009912 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +00009913 }
9914 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009915 while ((*ap2++ = *ap1++) != NULL)
9916 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009917#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +00009918 shellparam.optind = 1;
9919 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +00009920#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +00009921 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009922 return 0;
9923}
9924
Eric Andersencb57d552001-06-28 07:25:16 +00009925/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009926 * POSIX requires that 'set' (but not export or readonly) output the
9927 * variables in lexicographic order - by the locale's collating order (sigh).
9928 * Maybe we could keep them in an ordered balanced binary tree
9929 * instead of hashed lists.
9930 * For now just roll 'em through qsort for printing...
9931 */
9932static int
9933showvars(const char *sep_prefix, int on, int off)
9934{
9935 const char *sep;
9936 char **ep, **epend;
9937
9938 ep = listvars(on, off, &epend);
9939 qsort(ep, epend - ep, sizeof(char *), vpcmp);
9940
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009941 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009942
9943 for (; ep < epend; ep++) {
9944 const char *p;
9945 const char *q;
9946
9947 p = strchrnul(*ep, '=');
9948 q = nullstr;
9949 if (*p)
9950 q = single_quote(++p);
9951 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
9952 }
9953 return 0;
9954}
9955
9956/*
Eric Andersencb57d552001-06-28 07:25:16 +00009957 * The set command builtin.
9958 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009959static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009960setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00009961{
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009962 int retval;
9963
Denis Vlasenko68404f12008-03-17 09:00:54 +00009964 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +00009965 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009966 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009967 retval = 1;
9968 if (!options(0)) { /* if no parse error... */
9969 retval = 0;
9970 optschanged();
9971 if (*argptr != NULL) {
9972 setparam(argptr);
9973 }
Eric Andersencb57d552001-06-28 07:25:16 +00009974 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009975 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +00009976 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +00009977}
9978
Denis Vlasenko131ae172007-02-18 13:00:19 +00009979#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009980static void
9981change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +00009982{
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009983 /* Galois LFSR parameter */
9984 /* Taps at 32 31 29 1: */
9985 enum { MASK = 0x8000000b };
9986 /* Another example - taps at 32 31 30 10: */
9987 /* MASK = 0x00400007 */
9988
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009989 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +00009990 /* "get", generate */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009991 uint32_t t;
Eric Andersen16767e22004-03-16 05:14:10 +00009992
Denis Vlasenko448d30e2008-06-27 00:24:11 +00009993 /* LCG has period of 2^32 and alternating lowest bit */
9994 random_LCG = 1664525 * random_LCG + 1013904223;
9995 /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
9996 t = (random_galois_LFSR << 1);
9997 if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
9998 t ^= MASK;
9999 random_galois_LFSR = t;
Denis Vlasenkoce13b762008-06-29 02:25:53 +000010000 /* Both are weak, combining them gives better randomness
Denis Vlasenko448d30e2008-06-27 00:24:11 +000010001 * and ~2^64 period. & 0x7fff is probably bash compat
Denis Vlasenkoce13b762008-06-29 02:25:53 +000010002 * for $RANDOM range. Combining with subtraction is
10003 * just for fun. + and ^ would work equally well. */
10004 t = (t - random_LCG) & 0x7fff;
Eric Andersen16767e22004-03-16 05:14:10 +000010005 /* set without recursion */
Denis Vlasenko448d30e2008-06-27 00:24:11 +000010006 setvar(vrandom.text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010007 vrandom.flags &= ~VNOFUNC;
10008 } else {
10009 /* set/reset */
Denis Vlasenko448d30e2008-06-27 00:24:11 +000010010 random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
Eric Andersen16767e22004-03-16 05:14:10 +000010011 }
Eric Andersenef02f822004-03-11 13:34:24 +000010012}
Eric Andersen16767e22004-03-16 05:14:10 +000010013#endif
10014
Denis Vlasenko131ae172007-02-18 13:00:19 +000010015#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010016static int
Eric Andersenc470f442003-07-28 09:56:35 +000010017getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010018{
10019 char *p, *q;
10020 char c = '?';
10021 int done = 0;
10022 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010023 char s[12];
10024 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010025
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010026 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010027 return 1;
10028 optnext = optfirst + *param_optind - 1;
10029
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010030 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010031 p = NULL;
10032 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010033 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010034 if (p == NULL || *p == '\0') {
10035 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010036 p = *optnext;
10037 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010038 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010039 p = NULL;
10040 done = 1;
10041 goto out;
10042 }
10043 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010044 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010045 goto atend;
10046 }
10047
10048 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010049 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010050 if (*q == '\0') {
10051 if (optstr[0] == ':') {
10052 s[0] = c;
10053 s[1] = '\0';
10054 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010055 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010056 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010057 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010058 }
10059 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010060 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010061 }
10062 if (*++q == ':')
10063 q++;
10064 }
10065
10066 if (*++q == ':') {
10067 if (*p == '\0' && (p = *optnext) == NULL) {
10068 if (optstr[0] == ':') {
10069 s[0] = c;
10070 s[1] = '\0';
10071 err |= setvarsafe("OPTARG", s, 0);
10072 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010073 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010074 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010075 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010076 c = '?';
10077 }
Eric Andersenc470f442003-07-28 09:56:35 +000010078 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010079 }
10080
10081 if (p == *optnext)
10082 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010083 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010084 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010085 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010086 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010087 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010088 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010089 *param_optind = optnext - optfirst + 1;
10090 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010091 err |= setvarsafe("OPTIND", s, VNOFUNC);
10092 s[0] = c;
10093 s[1] = '\0';
10094 err |= setvarsafe(optvar, s, 0);
10095 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010096 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010097 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010098 flush_stdout_stderr();
10099 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010100 }
10101 return done;
10102}
Eric Andersenc470f442003-07-28 09:56:35 +000010103
10104/*
10105 * The getopts builtin. Shellparam.optnext points to the next argument
10106 * to be processed. Shellparam.optptr points to the next character to
10107 * be processed in the current argument. If shellparam.optnext is NULL,
10108 * then it's the first time getopts has been called.
10109 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010110static int
Eric Andersenc470f442003-07-28 09:56:35 +000010111getoptscmd(int argc, char **argv)
10112{
10113 char **optbase;
10114
10115 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010116 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010117 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010118 optbase = shellparam.p;
10119 if (shellparam.optind > shellparam.nparam + 1) {
10120 shellparam.optind = 1;
10121 shellparam.optoff = -1;
10122 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010123 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010124 optbase = &argv[3];
10125 if (shellparam.optind > argc - 2) {
10126 shellparam.optind = 1;
10127 shellparam.optoff = -1;
10128 }
10129 }
10130
10131 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010132 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010133}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010134#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010135
Eric Andersencb57d552001-06-28 07:25:16 +000010136
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010137/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010138
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010139struct heredoc {
10140 struct heredoc *next; /* next here document in list */
10141 union node *here; /* redirection node */
10142 char *eofmark; /* string indicating end of input */
10143 smallint striptabs; /* if set, strip leading tabs */
10144};
10145
10146static smallint tokpushback; /* last token pushed back */
10147static smallint parsebackquote; /* nonzero if we are inside backquotes */
10148static smallint quoteflag; /* set if (part of) last token was quoted */
10149static token_id_t lasttoken; /* last token read (integer id Txxx) */
10150static struct heredoc *heredoclist; /* list of here documents to read */
10151static char *wordtext; /* text of last word returned by readtoken */
10152static struct nodelist *backquotelist;
10153static union node *redirnode;
10154static struct heredoc *heredoc;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010155/*
10156 * NEOF is returned by parsecmd when it encounters an end of file. It
10157 * must be distinct from NULL, so we use the address of a variable that
10158 * happens to be handy.
10159 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010160#define NEOF ((union node *)&tokpushback)
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010161
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010162static void raise_error_syntax(const char *) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010163static void
10164raise_error_syntax(const char *msg)
10165{
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010166 ash_msg_and_raise_error("syntax error: %s", msg);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010167 /* NOTREACHED */
10168}
10169
10170/*
10171 * Called when an unexpected token is read during the parse. The argument
10172 * is the token that is expected, or -1 if more than one type of token can
10173 * occur at this point.
10174 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010175static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010176static void
10177raise_error_unexpected_syntax(int token)
10178{
10179 char msg[64];
10180 int l;
10181
Denis Vlasenko7b2294e2008-11-28 03:50:46 +000010182 l = sprintf(msg, "unexpected %s", tokname(lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010183 if (token >= 0)
10184 sprintf(msg + l, " (expecting %s)", tokname(token));
10185 raise_error_syntax(msg);
10186 /* NOTREACHED */
10187}
Eric Andersencb57d552001-06-28 07:25:16 +000010188
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010189#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010190
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010191/* parsing is heavily cross-recursive, need these forward decls */
10192static union node *andor(void);
10193static union node *pipeline(void);
10194static union node *parse_command(void);
10195static void parseheredoc(void);
10196static char peektoken(void);
10197static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010198
Eric Andersenc470f442003-07-28 09:56:35 +000010199static union node *
10200list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010201{
10202 union node *n1, *n2, *n3;
10203 int tok;
10204
Eric Andersenc470f442003-07-28 09:56:35 +000010205 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10206 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010207 return NULL;
10208 n1 = NULL;
10209 for (;;) {
10210 n2 = andor();
10211 tok = readtoken();
10212 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010213 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010214 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010215 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010216 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010217 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010218 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010219 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010220 n2 = n3;
10221 }
10222 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010223 }
10224 }
10225 if (n1 == NULL) {
10226 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010227 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010228 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010229 n3->type = NSEMI;
10230 n3->nbinary.ch1 = n1;
10231 n3->nbinary.ch2 = n2;
10232 n1 = n3;
10233 }
10234 switch (tok) {
10235 case TBACKGND:
10236 case TSEMI:
10237 tok = readtoken();
10238 /* fall through */
10239 case TNL:
10240 if (tok == TNL) {
10241 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010242 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010243 return n1;
10244 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010245 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010246 }
Eric Andersenc470f442003-07-28 09:56:35 +000010247 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010248 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010249 return n1;
10250 break;
10251 case TEOF:
10252 if (heredoclist)
10253 parseheredoc();
10254 else
Eric Andersenc470f442003-07-28 09:56:35 +000010255 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010256 return n1;
10257 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010258 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010259 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010260 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010261 return n1;
10262 }
10263 }
10264}
10265
Eric Andersenc470f442003-07-28 09:56:35 +000010266static union node *
10267andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010268{
Eric Andersencb57d552001-06-28 07:25:16 +000010269 union node *n1, *n2, *n3;
10270 int t;
10271
Eric Andersencb57d552001-06-28 07:25:16 +000010272 n1 = pipeline();
10273 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010274 t = readtoken();
10275 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010276 t = NAND;
10277 } else if (t == TOR) {
10278 t = NOR;
10279 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010280 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010281 return n1;
10282 }
Eric Andersenc470f442003-07-28 09:56:35 +000010283 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010284 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010285 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010286 n3->type = t;
10287 n3->nbinary.ch1 = n1;
10288 n3->nbinary.ch2 = n2;
10289 n1 = n3;
10290 }
10291}
10292
Eric Andersenc470f442003-07-28 09:56:35 +000010293static union node *
10294pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010295{
Eric Andersencb57d552001-06-28 07:25:16 +000010296 union node *n1, *n2, *pipenode;
10297 struct nodelist *lp, *prev;
10298 int negate;
10299
10300 negate = 0;
10301 TRACE(("pipeline: entered\n"));
10302 if (readtoken() == TNOT) {
10303 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010304 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010305 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010306 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010307 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010308 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010309 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010310 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010311 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010312 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010313 pipenode->npipe.cmdlist = lp;
10314 lp->n = n1;
10315 do {
10316 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010317 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010318 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010319 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010320 prev->next = lp;
10321 } while (readtoken() == TPIPE);
10322 lp->next = NULL;
10323 n1 = pipenode;
10324 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010325 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010326 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010327 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010328 n2->type = NNOT;
10329 n2->nnot.com = n1;
10330 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010331 }
10332 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010333}
10334
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010335static union node *
10336makename(void)
10337{
10338 union node *n;
10339
Denis Vlasenko597906c2008-02-20 16:38:54 +000010340 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010341 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010342 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010343 n->narg.text = wordtext;
10344 n->narg.backquote = backquotelist;
10345 return n;
10346}
10347
10348static void
10349fixredir(union node *n, const char *text, int err)
10350{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010351 int fd;
10352
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010353 TRACE(("Fix redir %s %d\n", text, err));
10354 if (!err)
10355 n->ndup.vname = NULL;
10356
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010357 fd = bb_strtou(text, NULL, 10);
10358 if (!errno && fd >= 0)
10359 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010360 else if (LONE_DASH(text))
10361 n->ndup.dupfd = -1;
10362 else {
10363 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010364 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010365 n->ndup.vname = makename();
10366 }
10367}
10368
10369/*
10370 * Returns true if the text contains nothing to expand (no dollar signs
10371 * or backquotes).
10372 */
10373static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010374noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010375{
Denis Vlasenko68819d12008-12-15 11:26:36 +000010376 const char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010377 char c;
10378
10379 p = text;
10380 while ((c = *p++) != '\0') {
10381 if (c == CTLQUOTEMARK)
10382 continue;
10383 if (c == CTLESC)
10384 p++;
Denis Vlasenko68819d12008-12-15 11:26:36 +000010385 else if (SIT((signed char)c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010386 return 0;
10387 }
10388 return 1;
10389}
10390
10391static void
10392parsefname(void)
10393{
10394 union node *n = redirnode;
10395
10396 if (readtoken() != TWORD)
10397 raise_error_unexpected_syntax(-1);
10398 if (n->type == NHERE) {
10399 struct heredoc *here = heredoc;
10400 struct heredoc *p;
10401 int i;
10402
10403 if (quoteflag == 0)
10404 n->type = NXHERE;
10405 TRACE(("Here document %d\n", n->type));
10406 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010407 raise_error_syntax("illegal eof marker for << redirection");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010408 rmescapes(wordtext);
10409 here->eofmark = wordtext;
10410 here->next = NULL;
10411 if (heredoclist == NULL)
10412 heredoclist = here;
10413 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010414 for (p = heredoclist; p->next; p = p->next)
10415 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010416 p->next = here;
10417 }
10418 } else if (n->type == NTOFD || n->type == NFROMFD) {
10419 fixredir(n, wordtext, 0);
10420 } else {
10421 n->nfile.fname = makename();
10422 }
10423}
Eric Andersencb57d552001-06-28 07:25:16 +000010424
Eric Andersenc470f442003-07-28 09:56:35 +000010425static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010426simplecmd(void)
10427{
10428 union node *args, **app;
10429 union node *n = NULL;
10430 union node *vars, **vpp;
10431 union node **rpp, *redir;
10432 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010433#if ENABLE_ASH_BASH_COMPAT
10434 smallint double_brackets_flag = 0;
10435#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010436
10437 args = NULL;
10438 app = &args;
10439 vars = NULL;
10440 vpp = &vars;
10441 redir = NULL;
10442 rpp = &redir;
10443
10444 savecheckkwd = CHKALIAS;
10445 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010446 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010447 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010448 t = readtoken();
10449 switch (t) {
10450#if ENABLE_ASH_BASH_COMPAT
10451 case TAND: /* "&&" */
10452 case TOR: /* "||" */
10453 if (!double_brackets_flag) {
10454 tokpushback = 1;
10455 goto out;
10456 }
10457 wordtext = (char *) (t == TAND ? "-a" : "-o");
10458#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010459 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010460 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010461 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010462 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010463 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010464#if ENABLE_ASH_BASH_COMPAT
10465 if (strcmp("[[", wordtext) == 0)
10466 double_brackets_flag = 1;
10467 else if (strcmp("]]", wordtext) == 0)
10468 double_brackets_flag = 0;
10469#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010470 n->narg.backquote = backquotelist;
10471 if (savecheckkwd && isassignment(wordtext)) {
10472 *vpp = n;
10473 vpp = &n->narg.next;
10474 } else {
10475 *app = n;
10476 app = &n->narg.next;
10477 savecheckkwd = 0;
10478 }
10479 break;
10480 case TREDIR:
10481 *rpp = n = redirnode;
10482 rpp = &n->nfile.next;
10483 parsefname(); /* read name of redirection file */
10484 break;
10485 case TLP:
10486 if (args && app == &args->narg.next
10487 && !vars && !redir
10488 ) {
10489 struct builtincmd *bcmd;
10490 const char *name;
10491
10492 /* We have a function */
10493 if (readtoken() != TRP)
10494 raise_error_unexpected_syntax(TRP);
10495 name = n->narg.text;
10496 if (!goodname(name)
10497 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10498 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010499 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010500 }
10501 n->type = NDEFUN;
10502 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10503 n->narg.next = parse_command();
10504 return n;
10505 }
10506 /* fall through */
10507 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010508 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010509 goto out;
10510 }
10511 }
10512 out:
10513 *app = NULL;
10514 *vpp = NULL;
10515 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010516 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010517 n->type = NCMD;
10518 n->ncmd.args = args;
10519 n->ncmd.assign = vars;
10520 n->ncmd.redirect = redir;
10521 return n;
10522}
10523
10524static union node *
10525parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010526{
Eric Andersencb57d552001-06-28 07:25:16 +000010527 union node *n1, *n2;
10528 union node *ap, **app;
10529 union node *cp, **cpp;
10530 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010531 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010532 int t;
10533
10534 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010535 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010536
Eric Andersencb57d552001-06-28 07:25:16 +000010537 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010538 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010539 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010540 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010541 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010542 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010543 n1->type = NIF;
10544 n1->nif.test = list(0);
10545 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010546 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010547 n1->nif.ifpart = list(0);
10548 n2 = n1;
10549 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010550 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010551 n2 = n2->nif.elsepart;
10552 n2->type = NIF;
10553 n2->nif.test = list(0);
10554 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010555 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010556 n2->nif.ifpart = list(0);
10557 }
10558 if (lasttoken == TELSE)
10559 n2->nif.elsepart = list(0);
10560 else {
10561 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010562 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010563 }
Eric Andersenc470f442003-07-28 09:56:35 +000010564 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010565 break;
10566 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010567 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010568 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010569 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010570 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010571 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010572 got = readtoken();
10573 if (got != TDO) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010574 TRACE(("expecting DO got %s %s\n", tokname(got),
10575 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010576 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010577 }
10578 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010579 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010580 break;
10581 }
10582 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010583 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010584 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010585 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010586 n1->type = NFOR;
10587 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010588 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010589 if (readtoken() == TIN) {
10590 app = &ap;
10591 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010592 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010593 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010594 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010595 n2->narg.text = wordtext;
10596 n2->narg.backquote = backquotelist;
10597 *app = n2;
10598 app = &n2->narg.next;
10599 }
10600 *app = NULL;
10601 n1->nfor.args = ap;
10602 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010603 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010604 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010605 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010606 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010607 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010608 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010609 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010610 n1->nfor.args = n2;
10611 /*
10612 * Newline or semicolon here is optional (but note
10613 * that the original Bourne shell only allowed NL).
10614 */
10615 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010616 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010617 }
Eric Andersenc470f442003-07-28 09:56:35 +000010618 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010619 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010620 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010621 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010622 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010623 break;
10624 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010625 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010626 n1->type = NCASE;
10627 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010628 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010629 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010630 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010631 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010632 n2->narg.text = wordtext;
10633 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010634 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010635 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010636 } while (readtoken() == TNL);
10637 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010638 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010639 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010640 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010641 checkkwd = CHKNL | CHKKWD;
10642 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010643 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010644 if (lasttoken == TLP)
10645 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010646 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010647 cp->type = NCLIST;
10648 app = &cp->nclist.pattern;
10649 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010650 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010651 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010652 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010653 ap->narg.text = wordtext;
10654 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010655 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010656 break;
10657 app = &ap->narg.next;
10658 readtoken();
10659 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010660 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010661 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010662 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010663 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010664
Eric Andersenc470f442003-07-28 09:56:35 +000010665 cpp = &cp->nclist.next;
10666
10667 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010668 t = readtoken();
10669 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010670 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010671 raise_error_unexpected_syntax(TENDCASE);
10672 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010673 }
Eric Andersenc470f442003-07-28 09:56:35 +000010674 }
Eric Andersencb57d552001-06-28 07:25:16 +000010675 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010676 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010677 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010678 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010679 n1->type = NSUBSHELL;
10680 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010681 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010682 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010683 break;
10684 case TBEGIN:
10685 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010686 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010687 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010688 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010689 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010690 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010691 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010692 }
10693
Eric Andersenc470f442003-07-28 09:56:35 +000010694 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010695 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010696
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010697 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010698 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010699 checkkwd = CHKKWD | CHKALIAS;
10700 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010701 while (readtoken() == TREDIR) {
10702 *rpp = n2 = redirnode;
10703 rpp = &n2->nfile.next;
10704 parsefname();
10705 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010706 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010707 *rpp = NULL;
10708 if (redir) {
10709 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010710 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010711 n2->type = NREDIR;
10712 n2->nredir.n = n1;
10713 n1 = n2;
10714 }
10715 n1->nredir.redirect = redir;
10716 }
Eric Andersencb57d552001-06-28 07:25:16 +000010717 return n1;
10718}
10719
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010720#if ENABLE_ASH_BASH_COMPAT
10721static int decode_dollar_squote(void)
10722{
10723 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10724 int c, cnt;
10725 char *p;
10726 char buf[4];
10727
10728 c = pgetc();
10729 p = strchr(C_escapes, c);
10730 if (p) {
10731 buf[0] = c;
10732 p = buf;
10733 cnt = 3;
10734 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10735 do {
10736 c = pgetc();
10737 *++p = c;
10738 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10739 pungetc();
10740 } else if (c == 'x') { /* \xHH */
10741 do {
10742 c = pgetc();
10743 *++p = c;
10744 } while (isxdigit(c) && --cnt);
10745 pungetc();
10746 if (cnt == 3) { /* \x but next char is "bad" */
10747 c = 'x';
10748 goto unrecognized;
10749 }
10750 } else { /* simple seq like \\ or \t */
10751 p++;
10752 }
10753 *p = '\0';
10754 p = buf;
10755 c = bb_process_escape_sequence((void*)&p);
10756 } else { /* unrecognized "\z": print both chars unless ' or " */
10757 if (c != '\'' && c != '"') {
10758 unrecognized:
10759 c |= 0x100; /* "please encode \, then me" */
10760 }
10761 }
10762 return c;
10763}
10764#endif
10765
Eric Andersencb57d552001-06-28 07:25:16 +000010766/*
10767 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10768 * is not NULL, read a here document. In the latter case, eofmark is the
10769 * word which marks the end of the document and striptabs is true if
10770 * leading tabs should be stripped from the document. The argument firstc
10771 * is the first character of the input token or document.
10772 *
10773 * Because C does not have internal subroutines, I have simulated them
10774 * using goto's to implement the subroutine linkage. The following macros
10775 * will run code that appears at the end of readtoken1.
10776 */
Eric Andersen2870d962001-07-02 17:27:21 +000010777#define CHECKEND() {goto checkend; checkend_return:;}
10778#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10779#define PARSESUB() {goto parsesub; parsesub_return:;}
10780#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10781#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10782#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000010783static int
Eric Andersenc470f442003-07-28 09:56:35 +000010784readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010785{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010786 /* NB: syntax parameter fits into smallint */
Eric Andersencb57d552001-06-28 07:25:16 +000010787 int c = firstc;
10788 char *out;
10789 int len;
10790 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010791 struct nodelist *bqlist;
10792 smallint quotef;
10793 smallint dblquote;
10794 smallint oldstyle;
10795 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000010796#if ENABLE_ASH_EXPAND_PRMT
10797 smallint pssyntax; /* we are expanding a prompt string */
10798#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010799 int varnest; /* levels of variables expansion */
10800 int arinest; /* levels of arithmetic expansion */
10801 int parenlevel; /* levels of parens in arithmetic */
10802 int dqvarnest; /* levels of variables expansion within double quotes */
10803
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010804 USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
10805
Eric Andersencb57d552001-06-28 07:25:16 +000010806#if __GNUC__
10807 /* Avoid longjmp clobbering */
10808 (void) &out;
10809 (void) &quotef;
10810 (void) &dblquote;
10811 (void) &varnest;
10812 (void) &arinest;
10813 (void) &parenlevel;
10814 (void) &dqvarnest;
10815 (void) &oldstyle;
10816 (void) &prevsyntax;
10817 (void) &syntax;
10818#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010819 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000010820 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010821 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010822 oldstyle = 0;
10823 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000010824#if ENABLE_ASH_EXPAND_PRMT
10825 pssyntax = (syntax == PSSYNTAX);
10826 if (pssyntax)
10827 syntax = DQSYNTAX;
10828#endif
10829 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010830 varnest = 0;
10831 arinest = 0;
10832 parenlevel = 0;
10833 dqvarnest = 0;
10834
10835 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000010836 loop:
10837 /* For each line, until end of word */
10838 {
Eric Andersenc470f442003-07-28 09:56:35 +000010839 CHECKEND(); /* set c to PEOF if at end of here document */
10840 for (;;) { /* until end of line or end of word */
10841 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000010842 switch (SIT(c, syntax)) {
Eric Andersenc470f442003-07-28 09:56:35 +000010843 case CNL: /* '\n' */
Eric Andersencb57d552001-06-28 07:25:16 +000010844 if (syntax == BASESYNTAX)
Eric Andersenc470f442003-07-28 09:56:35 +000010845 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010846 USTPUTC(c, out);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010847 g_parsefile->linno++;
Eric Andersencb57d552001-06-28 07:25:16 +000010848 if (doprompt)
10849 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010850 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000010851 goto loop; /* continue outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010852 case CWORD:
10853 USTPUTC(c, out);
10854 break;
10855 case CCTL:
Eric Andersenc470f442003-07-28 09:56:35 +000010856 if (eofmark == NULL || dblquote)
Eric Andersencb57d552001-06-28 07:25:16 +000010857 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010858#if ENABLE_ASH_BASH_COMPAT
10859 if (c == '\\' && bash_dollar_squote) {
10860 c = decode_dollar_squote();
10861 if (c & 0x100) {
10862 USTPUTC('\\', out);
10863 c = (unsigned char)c;
10864 }
10865 }
10866#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010867 USTPUTC(c, out);
10868 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010869 case CBACK: /* backslash */
Eric Andersencb57d552001-06-28 07:25:16 +000010870 c = pgetc2();
10871 if (c == PEOF) {
Eric Andersenc470f442003-07-28 09:56:35 +000010872 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010873 USTPUTC('\\', out);
10874 pungetc();
10875 } else if (c == '\n') {
10876 if (doprompt)
10877 setprompt(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010878 } else {
Denis Vlasenko46a53062007-09-24 18:30:02 +000010879#if ENABLE_ASH_EXPAND_PRMT
10880 if (c == '$' && pssyntax) {
10881 USTPUTC(CTLESC, out);
10882 USTPUTC('\\', out);
10883 }
10884#endif
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010885 if (dblquote && c != '\\'
10886 && c != '`' && c != '$'
10887 && (c != '"' || eofmark != NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000010888 ) {
10889 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010890 USTPUTC('\\', out);
Eric Andersenc470f442003-07-28 09:56:35 +000010891 }
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010892 if (SIT(c, SQSYNTAX) == CCTL)
Eric Andersencb57d552001-06-28 07:25:16 +000010893 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000010894 USTPUTC(c, out);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010895 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010896 }
10897 break;
10898 case CSQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010899 syntax = SQSYNTAX;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010900 quotemark:
Eric Andersenc470f442003-07-28 09:56:35 +000010901 if (eofmark == NULL) {
10902 USTPUTC(CTLQUOTEMARK, out);
10903 }
Eric Andersencb57d552001-06-28 07:25:16 +000010904 break;
10905 case CDQUOTE:
Eric Andersencb57d552001-06-28 07:25:16 +000010906 syntax = DQSYNTAX;
10907 dblquote = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010908 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010909 case CENDQUOTE:
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010910 USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010911 if (eofmark != NULL && arinest == 0
10912 && varnest == 0
10913 ) {
Eric Andersencb57d552001-06-28 07:25:16 +000010914 USTPUTC(c, out);
10915 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010916 if (dqvarnest == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010917 syntax = BASESYNTAX;
10918 dblquote = 0;
10919 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010920 quotef = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010921 goto quotemark;
Eric Andersencb57d552001-06-28 07:25:16 +000010922 }
10923 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010924 case CVAR: /* '$' */
10925 PARSESUB(); /* parse substitution */
Eric Andersencb57d552001-06-28 07:25:16 +000010926 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010927 case CENDVAR: /* '}' */
Eric Andersencb57d552001-06-28 07:25:16 +000010928 if (varnest > 0) {
10929 varnest--;
10930 if (dqvarnest > 0) {
10931 dqvarnest--;
10932 }
10933 USTPUTC(CTLENDVAR, out);
10934 } else {
10935 USTPUTC(c, out);
10936 }
10937 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010938#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000010939 case CLP: /* '(' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010940 parenlevel++;
10941 USTPUTC(c, out);
10942 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010943 case CRP: /* ')' in arithmetic */
Eric Andersencb57d552001-06-28 07:25:16 +000010944 if (parenlevel > 0) {
10945 USTPUTC(c, out);
10946 --parenlevel;
10947 } else {
10948 if (pgetc() == ')') {
10949 if (--arinest == 0) {
10950 USTPUTC(CTLENDARI, out);
10951 syntax = prevsyntax;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010952 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000010953 } else
10954 USTPUTC(')', out);
10955 } else {
10956 /*
10957 * unbalanced parens
10958 * (don't 2nd guess - no error)
10959 */
10960 pungetc();
10961 USTPUTC(')', out);
10962 }
10963 }
10964 break;
10965#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010966 case CBQUOTE: /* '`' */
Eric Andersencb57d552001-06-28 07:25:16 +000010967 PARSEBACKQOLD();
10968 break;
Eric Andersen2870d962001-07-02 17:27:21 +000010969 case CENDFILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010970 goto endword; /* exit outer loop */
Eric Andersencb57d552001-06-28 07:25:16 +000010971 case CIGN:
10972 break;
10973 default:
Denis Vlasenko834dee72008-10-07 09:18:30 +000010974 if (varnest == 0) {
10975#if ENABLE_ASH_BASH_COMPAT
10976 if (c == '&') {
10977 if (pgetc() == '>')
10978 c = 0x100 + '>'; /* flag &> */
10979 pungetc();
10980 }
10981#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010982 goto endword; /* exit outer loop */
Denis Vlasenko834dee72008-10-07 09:18:30 +000010983 }
Denis Vlasenko131ae172007-02-18 13:00:19 +000010984#if ENABLE_ASH_ALIAS
Eric Andersen3102ac42001-07-06 04:26:23 +000010985 if (c != PEOA)
10986#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010987 USTPUTC(c, out);
Eric Andersen3102ac42001-07-06 04:26:23 +000010988
Eric Andersencb57d552001-06-28 07:25:16 +000010989 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000010990 c = pgetc_fast();
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010991 } /* for (;;) */
Eric Andersencb57d552001-06-28 07:25:16 +000010992 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010993 endword:
Denis Vlasenko131ae172007-02-18 13:00:19 +000010994#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000010995 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010996 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000010997#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010998 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010999 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011000 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011001 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011002 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011003 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011004 }
11005 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011006 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011007 out = stackblock();
11008 if (eofmark == NULL) {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011009 if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
11010 && quotef == 0
11011 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011012 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011013 PARSEREDIR(); /* passed as params: out, c */
11014 lasttoken = TREDIR;
11015 return lasttoken;
11016 }
11017 /* else: non-number X seen, interpret it
11018 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011019 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011020 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011021 }
11022 quoteflag = quotef;
11023 backquotelist = bqlist;
11024 grabstackblock(len);
11025 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011026 lasttoken = TWORD;
11027 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011028/* end of readtoken routine */
11029
Eric Andersencb57d552001-06-28 07:25:16 +000011030/*
11031 * Check to see whether we are at the end of the here document. When this
11032 * is called, c is set to the first character of the next input line. If
11033 * we are at the end of the here document, this routine sets the c to PEOF.
11034 */
Eric Andersenc470f442003-07-28 09:56:35 +000011035checkend: {
11036 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011037#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011038 if (c == PEOA) {
11039 c = pgetc2();
11040 }
11041#endif
11042 if (striptabs) {
11043 while (c == '\t') {
Eric Andersencb57d552001-06-28 07:25:16 +000011044 c = pgetc2();
11045 }
Eric Andersenc470f442003-07-28 09:56:35 +000011046 }
11047 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011048 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011049 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011050
Eric Andersenc470f442003-07-28 09:56:35 +000011051 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011052 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11053 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011054 if (*p == '\n' && *q == '\0') {
11055 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011056 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011057 needprompt = doprompt;
11058 } else {
11059 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011060 }
11061 }
11062 }
11063 }
Eric Andersenc470f442003-07-28 09:56:35 +000011064 goto checkend_return;
11065}
Eric Andersencb57d552001-06-28 07:25:16 +000011066
Eric Andersencb57d552001-06-28 07:25:16 +000011067/*
11068 * Parse a redirection operator. The variable "out" points to a string
11069 * specifying the fd to be redirected. The variable "c" contains the
11070 * first character of the redirection operator.
11071 */
Eric Andersenc470f442003-07-28 09:56:35 +000011072parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011073 /* out is already checked to be a valid number or "" */
11074 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011075 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011076
Denis Vlasenko597906c2008-02-20 16:38:54 +000011077 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011078 if (c == '>') {
11079 np->nfile.fd = 1;
11080 c = pgetc();
11081 if (c == '>')
11082 np->type = NAPPEND;
11083 else if (c == '|')
11084 np->type = NCLOBBER;
11085 else if (c == '&')
11086 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011087 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011088 else {
11089 np->type = NTO;
11090 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011091 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011092 }
11093#if ENABLE_ASH_BASH_COMPAT
11094 else if (c == 0x100 + '>') { /* this flags &> redirection */
11095 np->nfile.fd = 1;
11096 pgetc(); /* this is '>', no need to check */
11097 np->type = NTO2;
11098 }
11099#endif
11100 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011101 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011102 c = pgetc();
11103 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011104 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011105 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011106 np = stzalloc(sizeof(struct nhere));
11107 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011108 }
11109 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011110 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011111 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011112 c = pgetc();
11113 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011114 heredoc->striptabs = 1;
11115 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011116 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011117 pungetc();
11118 }
11119 break;
11120
11121 case '&':
11122 np->type = NFROMFD;
11123 break;
11124
11125 case '>':
11126 np->type = NFROMTO;
11127 break;
11128
11129 default:
11130 np->type = NFROM;
11131 pungetc();
11132 break;
11133 }
Eric Andersencb57d552001-06-28 07:25:16 +000011134 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011135 if (fd >= 0)
11136 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011137 redirnode = np;
11138 goto parseredir_return;
11139}
Eric Andersencb57d552001-06-28 07:25:16 +000011140
Eric Andersencb57d552001-06-28 07:25:16 +000011141/*
11142 * Parse a substitution. At this point, we have read the dollar sign
11143 * and nothing else.
11144 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011145
11146/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11147 * (assuming ascii char codes, as the original implementation did) */
11148#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011149 (((unsigned)(c) - 33 < 32) \
11150 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011151parsesub: {
11152 int subtype;
11153 int typeloc;
11154 int flags;
11155 char *p;
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011156 static const char types[] ALIGN1 = "}-+?=";
Eric Andersencb57d552001-06-28 07:25:16 +000011157
Eric Andersenc470f442003-07-28 09:56:35 +000011158 c = pgetc();
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011159 if (c <= PEOA_OR_PEOF
11160 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011161 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011162#if ENABLE_ASH_BASH_COMPAT
11163 if (c == '\'')
11164 bash_dollar_squote = 1;
11165 else
11166#endif
11167 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011168 pungetc();
11169 } else if (c == '(') { /* $(command) or $((arith)) */
11170 if (pgetc() == '(') {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011171#if ENABLE_ASH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011172 PARSEARITH();
11173#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011174 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011175#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011176 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011177 pungetc();
11178 PARSEBACKQNEW();
11179 }
11180 } else {
11181 USTPUTC(CTLVAR, out);
11182 typeloc = out - (char *)stackblock();
11183 USTPUTC(VSNORMAL, out);
11184 subtype = VSNORMAL;
11185 if (c == '{') {
11186 c = pgetc();
11187 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011188 c = pgetc();
11189 if (c == '}')
Eric Andersenc470f442003-07-28 09:56:35 +000011190 c = '#';
11191 else
11192 subtype = VSLENGTH;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011193 } else
Eric Andersenc470f442003-07-28 09:56:35 +000011194 subtype = 0;
11195 }
11196 if (c > PEOA_OR_PEOF && is_name(c)) {
11197 do {
11198 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011199 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000011200 } while (c > PEOA_OR_PEOF && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011201 } else if (isdigit(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011202 do {
11203 STPUTC(c, out);
11204 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011205 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011206 } else if (is_special(c)) {
Eric Andersenc470f442003-07-28 09:56:35 +000011207 USTPUTC(c, out);
11208 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011209 } else {
11210 badsub:
11211 raise_error_syntax("bad substitution");
11212 }
Eric Andersencb57d552001-06-28 07:25:16 +000011213
Eric Andersenc470f442003-07-28 09:56:35 +000011214 STPUTC('=', out);
11215 flags = 0;
11216 if (subtype == 0) {
11217 switch (c) {
11218 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011219 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011220#if ENABLE_ASH_BASH_COMPAT
11221 if (c == ':' || c == '$' || isdigit(c)) {
11222 pungetc();
11223 subtype = VSSUBSTR;
11224 break;
11225 }
11226#endif
11227 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011228 /*FALLTHROUGH*/
11229 default:
11230 p = strchr(types, c);
11231 if (p == NULL)
11232 goto badsub;
11233 subtype = p - types + VSNORMAL;
11234 break;
11235 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011236 case '#': {
11237 int cc = c;
11238 subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
11239 c = pgetc();
11240 if (c == cc)
11241 subtype++;
11242 else
11243 pungetc();
11244 break;
11245 }
11246#if ENABLE_ASH_BASH_COMPAT
11247 case '/':
11248 subtype = VSREPLACE;
11249 c = pgetc();
11250 if (c == '/')
11251 subtype++; /* VSREPLACEALL */
11252 else
11253 pungetc();
11254 break;
11255#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011256 }
Eric Andersenc470f442003-07-28 09:56:35 +000011257 } else {
11258 pungetc();
11259 }
11260 if (dblquote || arinest)
11261 flags |= VSQUOTE;
11262 *((char *)stackblock() + typeloc) = subtype | flags;
11263 if (subtype != VSNORMAL) {
11264 varnest++;
11265 if (dblquote || arinest) {
11266 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011267 }
11268 }
11269 }
Eric Andersenc470f442003-07-28 09:56:35 +000011270 goto parsesub_return;
11271}
Eric Andersencb57d552001-06-28 07:25:16 +000011272
Eric Andersencb57d552001-06-28 07:25:16 +000011273/*
11274 * Called to parse command substitutions. Newstyle is set if the command
11275 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11276 * list of commands (passed by reference), and savelen is the number of
11277 * characters on the top of the stack which must be preserved.
11278 */
Eric Andersenc470f442003-07-28 09:56:35 +000011279parsebackq: {
11280 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011281 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011282 union node *n;
11283 char *volatile str;
11284 struct jmploc jmploc;
11285 struct jmploc *volatile savehandler;
11286 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011287 smallint saveprompt = 0;
11288
Eric Andersencb57d552001-06-28 07:25:16 +000011289#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011290 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011291#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011292 savepbq = parsebackquote;
11293 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011294 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011295 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011296 exception_handler = savehandler;
11297 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011298 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011299 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011300 str = NULL;
11301 savelen = out - (char *)stackblock();
11302 if (savelen > 0) {
11303 str = ckmalloc(savelen);
11304 memcpy(str, stackblock(), savelen);
11305 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011306 savehandler = exception_handler;
11307 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011308 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011309 if (oldstyle) {
11310 /* We must read until the closing backquote, giving special
11311 treatment to some slashes, and then push the string and
11312 reread it as input, interpreting it normally. */
11313 char *pout;
11314 int pc;
11315 size_t psavelen;
11316 char *pstr;
11317
11318
11319 STARTSTACKSTR(pout);
11320 for (;;) {
11321 if (needprompt) {
11322 setprompt(2);
Eric Andersenc470f442003-07-28 09:56:35 +000011323 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011324 pc = pgetc();
11325 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011326 case '`':
11327 goto done;
11328
11329 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011330 pc = pgetc();
11331 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011332 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011333 if (doprompt)
11334 setprompt(2);
11335 /*
11336 * If eating a newline, avoid putting
11337 * the newline into the new character
11338 * stream (via the STPUTC after the
11339 * switch).
11340 */
11341 continue;
11342 }
11343 if (pc != '\\' && pc != '`' && pc != '$'
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011344 && (!dblquote || pc != '"'))
Eric Andersenc470f442003-07-28 09:56:35 +000011345 STPUTC('\\', pout);
11346 if (pc > PEOA_OR_PEOF) {
11347 break;
11348 }
11349 /* fall through */
11350
11351 case PEOF:
Denis Vlasenko131ae172007-02-18 13:00:19 +000011352#if ENABLE_ASH_ALIAS
Eric Andersenc470f442003-07-28 09:56:35 +000011353 case PEOA:
11354#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011355 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011356 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011357
11358 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011359 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011360 needprompt = doprompt;
11361 break;
11362
11363 default:
11364 break;
11365 }
11366 STPUTC(pc, pout);
11367 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011368 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011369 STPUTC('\0', pout);
11370 psavelen = pout - (char *)stackblock();
11371 if (psavelen > 0) {
11372 pstr = grabstackstr(pout);
11373 setinputstring(pstr);
11374 }
11375 }
11376 nlpp = &bqlist;
11377 while (*nlpp)
11378 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011379 *nlpp = stzalloc(sizeof(**nlpp));
11380 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011381 parsebackquote = oldstyle;
11382
11383 if (oldstyle) {
11384 saveprompt = doprompt;
11385 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011386 }
11387
Eric Andersenc470f442003-07-28 09:56:35 +000011388 n = list(2);
11389
11390 if (oldstyle)
11391 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011392 else if (readtoken() != TRP)
11393 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011394
11395 (*nlpp)->n = n;
11396 if (oldstyle) {
11397 /*
11398 * Start reading from old file again, ignoring any pushed back
11399 * tokens left from the backquote parsing
11400 */
11401 popfile();
11402 tokpushback = 0;
11403 }
11404 while (stackblocksize() <= savelen)
11405 growstackblock();
11406 STARTSTACKSTR(out);
11407 if (str) {
11408 memcpy(out, str, savelen);
11409 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011410 INT_OFF;
11411 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011412 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011413 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011414 }
11415 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011416 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011417 if (arinest || dblquote)
11418 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11419 else
11420 USTPUTC(CTLBACKQ, out);
11421 if (oldstyle)
11422 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011423 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011424}
11425
Denis Vlasenko131ae172007-02-18 13:00:19 +000011426#if ENABLE_ASH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011427/*
11428 * Parse an arithmetic expansion (indicate start of one and set state)
11429 */
Eric Andersenc470f442003-07-28 09:56:35 +000011430parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011431 if (++arinest == 1) {
11432 prevsyntax = syntax;
11433 syntax = ARISYNTAX;
11434 USTPUTC(CTLARI, out);
11435 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011436 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011437 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011438 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011439 } else {
11440 /*
11441 * we collapse embedded arithmetic expansion to
11442 * parenthesis, which should be equivalent
11443 */
11444 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011445 }
Eric Andersenc470f442003-07-28 09:56:35 +000011446 goto parsearith_return;
11447}
11448#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011449
Eric Andersenc470f442003-07-28 09:56:35 +000011450} /* end of readtoken */
11451
Eric Andersencb57d552001-06-28 07:25:16 +000011452/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011453 * Read the next input token.
11454 * If the token is a word, we set backquotelist to the list of cmds in
11455 * backquotes. We set quoteflag to true if any part of the word was
11456 * quoted.
11457 * If the token is TREDIR, then we set redirnode to a structure containing
11458 * the redirection.
11459 * In all cases, the variable startlinno is set to the number of the line
11460 * on which the token starts.
11461 *
11462 * [Change comment: here documents and internal procedures]
11463 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11464 * word parsing code into a separate routine. In this case, readtoken
11465 * doesn't need to have any internal procedures, but parseword does.
11466 * We could also make parseoperator in essence the main routine, and
11467 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011468 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011469#define NEW_xxreadtoken
11470#ifdef NEW_xxreadtoken
11471/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011472static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011473 '\n', '(', ')', /* singles */
11474 '&', '|', ';', /* doubles */
11475 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011476};
Eric Andersencb57d552001-06-28 07:25:16 +000011477
Denis Vlasenko834dee72008-10-07 09:18:30 +000011478#define xxreadtoken_singles 3
11479#define xxreadtoken_doubles 3
11480
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011481static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011482 TNL, TLP, TRP, /* only single occurrence allowed */
11483 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11484 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011485 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011486};
11487
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011488static int
11489xxreadtoken(void)
11490{
11491 int c;
11492
11493 if (tokpushback) {
11494 tokpushback = 0;
11495 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011496 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011497 if (needprompt) {
11498 setprompt(2);
11499 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011500 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011501 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011502 c = pgetc_fast();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011503 if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
11504 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011505
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011506 if (c == '#') {
11507 while ((c = pgetc()) != '\n' && c != PEOF)
11508 continue;
11509 pungetc();
11510 } else if (c == '\\') {
11511 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011512 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011513 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011514 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011515 startlinno = ++g_parsefile->linno;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011516 if (doprompt)
11517 setprompt(2);
11518 } else {
11519 const char *p;
11520
11521 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11522 if (c != PEOF) {
11523 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011524 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011525 needprompt = doprompt;
11526 }
11527
11528 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011529 if (p == NULL)
11530 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011531
Denis Vlasenko834dee72008-10-07 09:18:30 +000011532 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11533 int cc = pgetc();
11534 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011535 p += xxreadtoken_doubles + 1;
11536 } else {
11537 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011538#if ENABLE_ASH_BASH_COMPAT
11539 if (c == '&' && cc == '>') /* &> */
11540 break; /* return readtoken1(...) */
11541#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011542 }
11543 }
11544 }
11545 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11546 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011547 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011548 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011549
11550 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011551}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011552#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011553#define RETURN(token) return lasttoken = token
11554static int
11555xxreadtoken(void)
11556{
11557 int c;
11558
11559 if (tokpushback) {
11560 tokpushback = 0;
11561 return lasttoken;
11562 }
11563 if (needprompt) {
11564 setprompt(2);
11565 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011566 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011567 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011568 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011569 switch (c) {
11570 case ' ': case '\t':
11571#if ENABLE_ASH_ALIAS
11572 case PEOA:
11573#endif
11574 continue;
11575 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011576 while ((c = pgetc()) != '\n' && c != PEOF)
11577 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011578 pungetc();
11579 continue;
11580 case '\\':
11581 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011582 startlinno = ++g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011583 if (doprompt)
11584 setprompt(2);
11585 continue;
11586 }
11587 pungetc();
11588 goto breakloop;
11589 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011590 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011591 needprompt = doprompt;
11592 RETURN(TNL);
11593 case PEOF:
11594 RETURN(TEOF);
11595 case '&':
11596 if (pgetc() == '&')
11597 RETURN(TAND);
11598 pungetc();
11599 RETURN(TBACKGND);
11600 case '|':
11601 if (pgetc() == '|')
11602 RETURN(TOR);
11603 pungetc();
11604 RETURN(TPIPE);
11605 case ';':
11606 if (pgetc() == ';')
11607 RETURN(TENDCASE);
11608 pungetc();
11609 RETURN(TSEMI);
11610 case '(':
11611 RETURN(TLP);
11612 case ')':
11613 RETURN(TRP);
11614 default:
11615 goto breakloop;
11616 }
11617 }
11618 breakloop:
11619 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11620#undef RETURN
11621}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011622#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011623
11624static int
11625readtoken(void)
11626{
11627 int t;
11628#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011629 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011630#endif
11631
11632#if ENABLE_ASH_ALIAS
11633 top:
11634#endif
11635
11636 t = xxreadtoken();
11637
11638 /*
11639 * eat newlines
11640 */
11641 if (checkkwd & CHKNL) {
11642 while (t == TNL) {
11643 parseheredoc();
11644 t = xxreadtoken();
11645 }
11646 }
11647
11648 if (t != TWORD || quoteflag) {
11649 goto out;
11650 }
11651
11652 /*
11653 * check for keywords
11654 */
11655 if (checkkwd & CHKKWD) {
11656 const char *const *pp;
11657
11658 pp = findkwd(wordtext);
11659 if (pp) {
11660 lasttoken = t = pp - tokname_array;
11661 TRACE(("keyword %s recognized\n", tokname(t)));
11662 goto out;
11663 }
11664 }
11665
11666 if (checkkwd & CHKALIAS) {
11667#if ENABLE_ASH_ALIAS
11668 struct alias *ap;
11669 ap = lookupalias(wordtext, 1);
11670 if (ap != NULL) {
11671 if (*ap->val) {
11672 pushstring(ap->val, ap);
11673 }
11674 goto top;
11675 }
11676#endif
11677 }
11678 out:
11679 checkkwd = 0;
11680#if DEBUG
11681 if (!alreadyseen)
11682 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11683 else
11684 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
11685#endif
11686 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011687}
11688
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011689static char
11690peektoken(void)
11691{
11692 int t;
11693
11694 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011695 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011696 return tokname_array[t][0];
11697}
Eric Andersencb57d552001-06-28 07:25:16 +000011698
11699/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011700 * Read and parse a command. Returns NEOF on end of file. (NULL is a
11701 * valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011702 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011703static union node *
11704parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011705{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011706 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011707
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011708 tokpushback = 0;
11709 doprompt = interact;
11710 if (doprompt)
11711 setprompt(doprompt);
11712 needprompt = 0;
11713 t = readtoken();
11714 if (t == TEOF)
11715 return NEOF;
11716 if (t == TNL)
11717 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011718 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011719 return list(1);
11720}
11721
11722/*
11723 * Input any here documents.
11724 */
11725static void
11726parseheredoc(void)
11727{
11728 struct heredoc *here;
11729 union node *n;
11730
11731 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011732 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011733
11734 while (here) {
11735 if (needprompt) {
11736 setprompt(2);
11737 }
11738 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
11739 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011740 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011742 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011743 n->narg.text = wordtext;
11744 n->narg.backquote = backquotelist;
11745 here->here->nhere.doc = n;
11746 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011747 }
Eric Andersencb57d552001-06-28 07:25:16 +000011748}
11749
11750
11751/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011752 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011753 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011754#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011755static const char *
11756expandstr(const char *ps)
11757{
11758 union node n;
11759
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011760 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11761 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011762 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011763 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011764 popfile();
11765
11766 n.narg.type = NARG;
11767 n.narg.next = NULL;
11768 n.narg.text = wordtext;
11769 n.narg.backquote = backquotelist;
11770
11771 expandarg(&n, NULL, 0);
11772 return stackblock();
11773}
11774#endif
11775
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011776/*
11777 * Execute a command or commands contained in a string.
11778 */
11779static int
11780evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011781{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011782 union node *n;
11783 struct stackmark smark;
11784 int skip;
11785
11786 setinputstring(s);
11787 setstackmark(&smark);
11788
11789 skip = 0;
11790 while ((n = parsecmd(0)) != NEOF) {
11791 evaltree(n, 0);
11792 popstackmark(&smark);
11793 skip = evalskip;
11794 if (skip)
11795 break;
11796 }
11797 popfile();
11798
11799 skip &= mask;
11800 evalskip = skip;
11801 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000011802}
11803
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011804/*
11805 * The eval command.
11806 */
11807static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011808evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011809{
11810 char *p;
11811 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011812
Denis Vlasenko68404f12008-03-17 09:00:54 +000011813 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011814 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000011815 argv += 2;
11816 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011817 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011818 for (;;) {
11819 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000011820 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011821 if (p == NULL)
11822 break;
11823 STPUTC(' ', concat);
11824 }
11825 STPUTC('\0', concat);
11826 p = grabstackstr(concat);
11827 }
11828 evalstring(p, ~SKIPEVAL);
11829
11830 }
11831 return exitstatus;
11832}
11833
11834/*
11835 * Read and execute commands. "Top" is nonzero for the top level command
11836 * loop; it turns on prompting if the shell is interactive.
11837 */
11838static int
11839cmdloop(int top)
11840{
11841 union node *n;
11842 struct stackmark smark;
11843 int inter;
11844 int numeof = 0;
11845
11846 TRACE(("cmdloop(%d) called\n", top));
11847 for (;;) {
11848 int skip;
11849
11850 setstackmark(&smark);
11851#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011852 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011853 showjobs(stderr, SHOW_CHANGED);
11854#endif
11855 inter = 0;
11856 if (iflag && top) {
11857 inter++;
11858#if ENABLE_ASH_MAIL
11859 chkmail();
11860#endif
11861 }
11862 n = parsecmd(inter);
11863 /* showtree(n); DEBUG */
11864 if (n == NEOF) {
11865 if (!top || numeof >= 50)
11866 break;
11867 if (!stoppedjobs()) {
11868 if (!Iflag)
11869 break;
11870 out2str("\nUse \"exit\" to leave shell.\n");
11871 }
11872 numeof++;
11873 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000011874 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
11875 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011876 numeof = 0;
11877 evaltree(n, 0);
11878 }
11879 popstackmark(&smark);
11880 skip = evalskip;
11881
11882 if (skip) {
11883 evalskip = 0;
11884 return skip & SKIPEVAL;
11885 }
11886 }
11887 return 0;
11888}
11889
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011890/*
11891 * Take commands from a file. To be compatible we should do a path
11892 * search for the file, which is necessary to find sub-commands.
11893 */
11894static char *
11895find_dot_file(char *name)
11896{
11897 char *fullname;
11898 const char *path = pathval();
11899 struct stat statb;
11900
11901 /* don't try this for absolute or relative paths */
11902 if (strchr(name, '/'))
11903 return name;
11904
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011905 /* IIRC standards do not say whether . is to be searched.
11906 * And it is even smaller this way, making it unconditional for now:
11907 */
11908 if (1) { /* ENABLE_ASH_BASH_COMPAT */
11909 fullname = name;
11910 goto try_cur_dir;
11911 }
11912
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011913 while ((fullname = padvance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000011914 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011915 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
11916 /*
11917 * Don't bother freeing here, since it will
11918 * be freed by the caller.
11919 */
11920 return fullname;
11921 }
11922 stunalloc(fullname);
11923 }
11924
11925 /* not found in the PATH */
11926 ash_msg_and_raise_error("%s: not found", name);
11927 /* NOTREACHED */
11928}
11929
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011930static int
11931dotcmd(int argc, char **argv)
11932{
11933 struct strlist *sp;
11934 volatile struct shparam saveparam;
11935 int status = 0;
11936
11937 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000011938 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011939
Denis Vlasenko68404f12008-03-17 09:00:54 +000011940 if (argv[1]) { /* That's what SVR2 does */
11941 char *fullname = find_dot_file(argv[1]);
11942 argv += 2;
11943 argc -= 2;
11944 if (argc) { /* argc > 0, argv[0] != NULL */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011945 saveparam = shellparam;
Denis Vlasenko01631112007-12-16 17:20:38 +000011946 shellparam.malloced = 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011947 shellparam.nparam = argc;
11948 shellparam.p = argv;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011949 };
11950
11951 setinputfile(fullname, INPUT_PUSH_FILE);
11952 commandname = fullname;
11953 cmdloop(0);
11954 popfile();
11955
Denis Vlasenko68404f12008-03-17 09:00:54 +000011956 if (argc) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011957 freeparam(&shellparam);
11958 shellparam = saveparam;
11959 };
11960 status = exitstatus;
11961 }
11962 return status;
11963}
11964
11965static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011966exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011967{
11968 if (stoppedjobs())
11969 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011970 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011971 exitstatus = number(argv[1]);
11972 raise_exception(EXEXIT);
11973 /* NOTREACHED */
11974}
11975
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011976/*
11977 * Read a file containing shell functions.
11978 */
11979static void
11980readcmdfile(char *name)
11981{
11982 setinputfile(name, INPUT_PUSH_FILE);
11983 cmdloop(0);
11984 popfile();
11985}
11986
11987
Denis Vlasenkocc571512007-02-23 21:10:35 +000011988/* ============ find_command inplementation */
11989
11990/*
11991 * Resolve a command name. If you change this routine, you may have to
11992 * change the shellexec routine as well.
11993 */
11994static void
11995find_command(char *name, struct cmdentry *entry, int act, const char *path)
11996{
11997 struct tblentry *cmdp;
11998 int idx;
11999 int prev;
12000 char *fullname;
12001 struct stat statb;
12002 int e;
12003 int updatetbl;
12004 struct builtincmd *bcmd;
12005
12006 /* If name contains a slash, don't use PATH or hash table */
12007 if (strchr(name, '/') != NULL) {
12008 entry->u.index = -1;
12009 if (act & DO_ABS) {
12010 while (stat(name, &statb) < 0) {
12011#ifdef SYSV
12012 if (errno == EINTR)
12013 continue;
12014#endif
12015 entry->cmdtype = CMDUNKNOWN;
12016 return;
12017 }
12018 }
12019 entry->cmdtype = CMDNORMAL;
12020 return;
12021 }
12022
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012023/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012024
12025 updatetbl = (path == pathval());
12026 if (!updatetbl) {
12027 act |= DO_ALTPATH;
12028 if (strstr(path, "%builtin") != NULL)
12029 act |= DO_ALTBLTIN;
12030 }
12031
12032 /* If name is in the table, check answer will be ok */
12033 cmdp = cmdlookup(name, 0);
12034 if (cmdp != NULL) {
12035 int bit;
12036
12037 switch (cmdp->cmdtype) {
12038 default:
12039#if DEBUG
12040 abort();
12041#endif
12042 case CMDNORMAL:
12043 bit = DO_ALTPATH;
12044 break;
12045 case CMDFUNCTION:
12046 bit = DO_NOFUNC;
12047 break;
12048 case CMDBUILTIN:
12049 bit = DO_ALTBLTIN;
12050 break;
12051 }
12052 if (act & bit) {
12053 updatetbl = 0;
12054 cmdp = NULL;
12055 } else if (cmdp->rehash == 0)
12056 /* if not invalidated by cd, we're done */
12057 goto success;
12058 }
12059
12060 /* If %builtin not in path, check for builtin next */
12061 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012062 if (bcmd) {
12063 if (IS_BUILTIN_REGULAR(bcmd))
12064 goto builtin_success;
12065 if (act & DO_ALTPATH) {
12066 if (!(act & DO_ALTBLTIN))
12067 goto builtin_success;
12068 } else if (builtinloc <= 0) {
12069 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012070 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012071 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012072
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012073#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012074 {
12075 int applet_no = find_applet_by_name(name);
12076 if (applet_no >= 0) {
12077 entry->cmdtype = CMDNORMAL;
12078 entry->u.index = -2 - applet_no;
12079 return;
12080 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012081 }
12082#endif
12083
Denis Vlasenkocc571512007-02-23 21:10:35 +000012084 /* We have to search path. */
12085 prev = -1; /* where to start */
12086 if (cmdp && cmdp->rehash) { /* doing a rehash */
12087 if (cmdp->cmdtype == CMDBUILTIN)
12088 prev = builtinloc;
12089 else
12090 prev = cmdp->param.index;
12091 }
12092
12093 e = ENOENT;
12094 idx = -1;
12095 loop:
12096 while ((fullname = padvance(&path, name)) != NULL) {
12097 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012098 /* NB: code below will still use fullname
12099 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012100 idx++;
12101 if (pathopt) {
12102 if (prefix(pathopt, "builtin")) {
12103 if (bcmd)
12104 goto builtin_success;
12105 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012106 }
12107 if ((act & DO_NOFUNC)
12108 || !prefix(pathopt, "func")
12109 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012110 continue;
12111 }
12112 }
12113 /* if rehash, don't redo absolute path names */
12114 if (fullname[0] == '/' && idx <= prev) {
12115 if (idx < prev)
12116 continue;
12117 TRACE(("searchexec \"%s\": no change\n", name));
12118 goto success;
12119 }
12120 while (stat(fullname, &statb) < 0) {
12121#ifdef SYSV
12122 if (errno == EINTR)
12123 continue;
12124#endif
12125 if (errno != ENOENT && errno != ENOTDIR)
12126 e = errno;
12127 goto loop;
12128 }
12129 e = EACCES; /* if we fail, this will be the error */
12130 if (!S_ISREG(statb.st_mode))
12131 continue;
12132 if (pathopt) { /* this is a %func directory */
12133 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012134 /* NB: stalloc will return space pointed by fullname
12135 * (because we don't have any intervening allocations
12136 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012137 readcmdfile(fullname);
12138 cmdp = cmdlookup(name, 0);
12139 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12140 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12141 stunalloc(fullname);
12142 goto success;
12143 }
12144 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12145 if (!updatetbl) {
12146 entry->cmdtype = CMDNORMAL;
12147 entry->u.index = idx;
12148 return;
12149 }
12150 INT_OFF;
12151 cmdp = cmdlookup(name, 1);
12152 cmdp->cmdtype = CMDNORMAL;
12153 cmdp->param.index = idx;
12154 INT_ON;
12155 goto success;
12156 }
12157
12158 /* We failed. If there was an entry for this command, delete it */
12159 if (cmdp && updatetbl)
12160 delete_cmd_entry();
12161 if (act & DO_ERR)
12162 ash_msg("%s: %s", name, errmsg(e, "not found"));
12163 entry->cmdtype = CMDUNKNOWN;
12164 return;
12165
12166 builtin_success:
12167 if (!updatetbl) {
12168 entry->cmdtype = CMDBUILTIN;
12169 entry->u.cmd = bcmd;
12170 return;
12171 }
12172 INT_OFF;
12173 cmdp = cmdlookup(name, 1);
12174 cmdp->cmdtype = CMDBUILTIN;
12175 cmdp->param.cmd = bcmd;
12176 INT_ON;
12177 success:
12178 cmdp->rehash = 0;
12179 entry->cmdtype = cmdp->cmdtype;
12180 entry->u = cmdp->param;
12181}
12182
12183
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012184/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012185
Eric Andersencb57d552001-06-28 07:25:16 +000012186/*
Eric Andersencb57d552001-06-28 07:25:16 +000012187 * The trap builtin.
12188 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012189static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012190trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012191{
12192 char *action;
12193 char **ap;
12194 int signo;
12195
Eric Andersenc470f442003-07-28 09:56:35 +000012196 nextopt(nullstr);
12197 ap = argptr;
12198 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012199 for (signo = 0; signo < NSIG; signo++) {
Eric Andersencb57d552001-06-28 07:25:16 +000012200 if (trap[signo] != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012201 out1fmt("trap -- %s %s\n",
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012202 single_quote(trap[signo]),
12203 get_signame(signo));
Eric Andersencb57d552001-06-28 07:25:16 +000012204 }
12205 }
12206 return 0;
12207 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012208 action = NULL;
12209 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012210 action = *ap++;
12211 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012212 signo = get_signum(*ap);
12213 if (signo < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012214 ash_msg_and_raise_error("%s: bad trap", *ap);
12215 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012216 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012217 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012218 action = NULL;
12219 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012220 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012221 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012222 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000012223 trap[signo] = action;
12224 if (signo != 0)
12225 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012226 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000012227 ap++;
12228 }
12229 return 0;
12230}
12231
Eric Andersenc470f442003-07-28 09:56:35 +000012232
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012233/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012234
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012235#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012236/*
12237 * Lists available builtins
12238 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012239static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012240helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012241{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012242 unsigned col;
12243 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012244
12245 out1fmt("\nBuilt-in commands:\n-------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012246 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012247 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012248 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012249 if (col > 60) {
12250 out1fmt("\n");
12251 col = 0;
12252 }
12253 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012254#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012255 {
12256 const char *a = applet_names;
12257 while (*a) {
12258 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12259 if (col > 60) {
12260 out1fmt("\n");
12261 col = 0;
12262 }
12263 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012264 }
12265 }
12266#endif
12267 out1fmt("\n\n");
12268 return EXIT_SUCCESS;
12269}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012270#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012271
Eric Andersencb57d552001-06-28 07:25:16 +000012272/*
Eric Andersencb57d552001-06-28 07:25:16 +000012273 * The export and readonly commands.
12274 */
Eric Andersenc470f442003-07-28 09:56:35 +000012275static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012276exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012277{
12278 struct var *vp;
12279 char *name;
12280 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012281 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012282 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012283
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012284 if (nextopt("p") != 'p') {
12285 aptr = argptr;
12286 name = *aptr;
12287 if (name) {
12288 do {
12289 p = strchr(name, '=');
12290 if (p != NULL) {
12291 p++;
12292 } else {
12293 vp = *findvar(hashvar(name), name);
12294 if (vp) {
12295 vp->flags |= flag;
12296 continue;
12297 }
Eric Andersencb57d552001-06-28 07:25:16 +000012298 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012299 setvar(name, p, flag);
12300 } while ((name = *++aptr) != NULL);
12301 return 0;
12302 }
Eric Andersencb57d552001-06-28 07:25:16 +000012303 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012304 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012305 return 0;
12306}
12307
Eric Andersencb57d552001-06-28 07:25:16 +000012308/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012309 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012310 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012311static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012312unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012313{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012314 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012315
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012316 cmdp = cmdlookup(name, 0);
12317 if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
12318 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012319}
12320
Eric Andersencb57d552001-06-28 07:25:16 +000012321/*
Eric Andersencb57d552001-06-28 07:25:16 +000012322 * The unset builtin command. We unset the function before we unset the
12323 * variable to allow a function to be unset when there is a readonly variable
12324 * with the same name.
12325 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012326static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012327unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012328{
12329 char **ap;
12330 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012331 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012332 int ret = 0;
12333
12334 while ((i = nextopt("vf")) != '\0') {
Eric Andersenc470f442003-07-28 09:56:35 +000012335 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012336 }
Eric Andersencb57d552001-06-28 07:25:16 +000012337
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012338 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012339 if (flag != 'f') {
12340 i = unsetvar(*ap);
12341 ret |= i;
12342 if (!(i & 2))
12343 continue;
12344 }
12345 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012346 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012347 }
Eric Andersenc470f442003-07-28 09:56:35 +000012348 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012349}
12350
12351
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000012352/* setmode.c */
Eric Andersencb57d552001-06-28 07:25:16 +000012353
Eric Andersenc470f442003-07-28 09:56:35 +000012354#include <sys/times.h>
12355
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012356static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012357 ' ', offsetof(struct tms, tms_utime),
12358 '\n', offsetof(struct tms, tms_stime),
12359 ' ', offsetof(struct tms, tms_cutime),
12360 '\n', offsetof(struct tms, tms_cstime),
12361 0
12362};
Eric Andersencb57d552001-06-28 07:25:16 +000012363
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012364static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012365timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012366{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012367 long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012368 const unsigned char *p;
12369 struct tms buf;
12370
12371 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012372 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012373
12374 p = timescmd_str;
12375 do {
12376 t = *(clock_t *)(((char *) &buf) + p[1]);
12377 s = t / clk_tck;
12378 out1fmt("%ldm%ld.%.3lds%c",
12379 s/60, s%60,
12380 ((t - s * clk_tck) * 1000) / clk_tck,
12381 p[0]);
12382 } while (*(p += 2));
12383
Eric Andersencb57d552001-06-28 07:25:16 +000012384 return 0;
12385}
12386
Denis Vlasenko131ae172007-02-18 13:00:19 +000012387#if ENABLE_ASH_MATH_SUPPORT
Eric Andersened9ecf72004-06-22 08:29:45 +000012388static arith_t
Eric Andersenc470f442003-07-28 09:56:35 +000012389dash_arith(const char *s)
Eric Andersen74bcd162001-07-30 21:41:37 +000012390{
Eric Andersened9ecf72004-06-22 08:29:45 +000012391 arith_t result;
Eric Andersenc470f442003-07-28 09:56:35 +000012392 int errcode = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012393
Denis Vlasenkob012b102007-02-19 22:43:01 +000012394 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012395 result = arith(s, &errcode);
12396 if (errcode < 0) {
Eric Andersen90898442003-08-06 11:20:52 +000012397 if (errcode == -3)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012398 ash_msg_and_raise_error("exponent less than 0");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012399 if (errcode == -2)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012400 ash_msg_and_raise_error("divide by zero");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012401 if (errcode == -5)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012402 ash_msg_and_raise_error("expression recursion loop detected");
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012403 raise_error_syntax(s);
Eric Andersenc470f442003-07-28 09:56:35 +000012404 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012405 INT_ON;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012406
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012407 return result;
Eric Andersen74bcd162001-07-30 21:41:37 +000012408}
Eric Andersenc470f442003-07-28 09:56:35 +000012409
Eric Andersenc470f442003-07-28 09:56:35 +000012410/*
Eric Andersen90898442003-08-06 11:20:52 +000012411 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12412 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12413 *
12414 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012415 */
12416static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012417letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012418{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012419 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012420
Denis Vlasenko68404f12008-03-17 09:00:54 +000012421 argv++;
12422 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012423 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012424 do {
12425 i = dash_arith(*argv);
12426 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012427
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012428 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012429}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012430#endif /* ASH_MATH_SUPPORT */
Eric Andersenc470f442003-07-28 09:56:35 +000012431
Eric Andersenc470f442003-07-28 09:56:35 +000012432
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012433/* ============ miscbltin.c
12434 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012435 * Miscellaneous builtins.
Eric Andersenc470f442003-07-28 09:56:35 +000012436 */
12437
12438#undef rflag
12439
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000012440#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
Eric Andersenc470f442003-07-28 09:56:35 +000012441typedef enum __rlimit_resource rlim_t;
12442#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012443
Eric Andersenc470f442003-07-28 09:56:35 +000012444/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012445 * The read builtin. Options:
12446 * -r Do not interpret '\' specially
12447 * -s Turn off echo (tty only)
12448 * -n NCHARS Read NCHARS max
12449 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12450 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12451 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012452 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012453 * TODO: bash also has:
12454 * -a ARRAY Read into array[0],[1],etc
12455 * -d DELIM End on DELIM char, not newline
12456 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012457 */
Eric Andersenc470f442003-07-28 09:56:35 +000012458static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012459readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012460{
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012461 static const char *const arg_REPLY[] = { "REPLY", NULL };
12462
Eric Andersenc470f442003-07-28 09:56:35 +000012463 char **ap;
12464 int backslash;
12465 char c;
12466 int rflag;
12467 char *prompt;
12468 const char *ifs;
12469 char *p;
12470 int startword;
12471 int status;
12472 int i;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012473 int fd = 0;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012474#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012475 int nchars = 0; /* if != 0, -n is in effect */
Paul Fox02eb9342005-09-07 16:56:02 +000012476 int silent = 0;
12477 struct termios tty, old_tty;
12478#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012479#if ENABLE_ASH_READ_TIMEOUT
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012480 unsigned end_ms = 0;
12481 unsigned timeout = 0;
Paul Fox02eb9342005-09-07 16:56:02 +000012482#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012483
12484 rflag = 0;
12485 prompt = NULL;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012486 while ((i = nextopt("p:u:r"
12487 USE_ASH_READ_TIMEOUT("t:")
12488 USE_ASH_READ_NCHARS("n:s")
12489 )) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012490 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012491 case 'p':
Eric Andersenc470f442003-07-28 09:56:35 +000012492 prompt = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012493 break;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012494#if ENABLE_ASH_READ_NCHARS
Paul Fox02eb9342005-09-07 16:56:02 +000012495 case 'n':
Denis Vlasenko037576d2007-10-20 18:30:38 +000012496 nchars = bb_strtou(optionarg, NULL, 10);
12497 if (nchars < 0 || errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012498 ash_msg_and_raise_error("invalid count");
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012499 /* nchars == 0: off (bash 3.2 does this too) */
Paul Fox02eb9342005-09-07 16:56:02 +000012500 break;
12501 case 's':
12502 silent = 1;
12503 break;
Ned Ludd2123b7c2005-02-09 21:07:23 +000012504#endif
Denis Vlasenko131ae172007-02-18 13:00:19 +000012505#if ENABLE_ASH_READ_TIMEOUT
Paul Fox02eb9342005-09-07 16:56:02 +000012506 case 't':
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012507 timeout = bb_strtou(optionarg, NULL, 10);
12508 if (errno || timeout > UINT_MAX / 2048)
12509 ash_msg_and_raise_error("invalid timeout");
12510 timeout *= 1000;
12511#if 0 /* even bash have no -t N.NNN support */
Denis Vlasenko037576d2007-10-20 18:30:38 +000012512 ts.tv_sec = bb_strtou(optionarg, &p, 10);
Paul Fox02eb9342005-09-07 16:56:02 +000012513 ts.tv_usec = 0;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012514 /* EINVAL means number is ok, but not terminated by NUL */
12515 if (*p == '.' && errno == EINVAL) {
Paul Fox02eb9342005-09-07 16:56:02 +000012516 char *p2;
12517 if (*++p) {
12518 int scale;
Denis Vlasenko037576d2007-10-20 18:30:38 +000012519 ts.tv_usec = bb_strtou(p, &p2, 10);
12520 if (errno)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012521 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012522 scale = p2 - p;
12523 /* normalize to usec */
12524 if (scale > 6)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012525 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012526 while (scale++ < 6)
12527 ts.tv_usec *= 10;
12528 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012529 } else if (ts.tv_sec < 0 || errno) {
Denis Vlasenkob012b102007-02-19 22:43:01 +000012530 ash_msg_and_raise_error("invalid timeout");
Paul Fox02eb9342005-09-07 16:56:02 +000012531 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012532 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
Denis Vlasenkob012b102007-02-19 22:43:01 +000012533 ash_msg_and_raise_error("invalid timeout");
Denis Vlasenko037576d2007-10-20 18:30:38 +000012534 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012535#endif /* if 0 */
Paul Fox02eb9342005-09-07 16:56:02 +000012536 break;
12537#endif
12538 case 'r':
12539 rflag = 1;
12540 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012541 case 'u':
12542 fd = bb_strtou(optionarg, NULL, 10);
12543 if (fd < 0 || errno)
12544 ash_msg_and_raise_error("invalid file descriptor");
12545 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012546 default:
12547 break;
12548 }
Eric Andersenc470f442003-07-28 09:56:35 +000012549 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012550 if (prompt && isatty(fd)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012551 out2str(prompt);
Eric Andersenc470f442003-07-28 09:56:35 +000012552 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012553 ap = argptr;
12554 if (*ap == NULL)
Denis Vlasenko9cd4c762008-06-18 19:22:19 +000012555 ap = (char**)arg_REPLY;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012556 ifs = bltinlookup("IFS");
12557 if (ifs == NULL)
Eric Andersenc470f442003-07-28 09:56:35 +000012558 ifs = defifs;
Denis Vlasenko131ae172007-02-18 13:00:19 +000012559#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012560 tcgetattr(fd, &tty);
12561 old_tty = tty;
12562 if (nchars || silent) {
12563 if (nchars) {
12564 tty.c_lflag &= ~ICANON;
12565 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
Paul Fox02eb9342005-09-07 16:56:02 +000012566 }
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012567 if (silent) {
12568 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
12569 }
12570 /* if tcgetattr failed, tcsetattr will fail too.
12571 * Ignoring, it's harmless. */
12572 tcsetattr(fd, TCSANOW, &tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012573 }
12574#endif
Paul Fox02eb9342005-09-07 16:56:02 +000012575
Eric Andersenc470f442003-07-28 09:56:35 +000012576 status = 0;
12577 startword = 1;
12578 backslash = 0;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012579#if ENABLE_ASH_READ_TIMEOUT
12580 if (timeout) /* NB: ensuring end_ms is nonzero */
12581 end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
12582#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012583 STARTSTACKSTR(p);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012584 do {
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012585#if ENABLE_ASH_READ_TIMEOUT
12586 if (end_ms) {
12587 struct pollfd pfd[1];
12588 pfd[0].fd = fd;
12589 pfd[0].events = POLLIN;
12590 timeout = end_ms - (unsigned)(monotonic_us() / 1000);
12591 if ((int)timeout <= 0 /* already late? */
12592 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
12593 ) { /* timed out! */
12594#if ENABLE_ASH_READ_NCHARS
12595 tcsetattr(fd, TCSANOW, &old_tty);
12596#endif
12597 return 1;
12598 }
12599 }
12600#endif
12601 if (nonblock_safe_read(fd, &c, 1) != 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000012602 status = 1;
12603 break;
12604 }
12605 if (c == '\0')
12606 continue;
12607 if (backslash) {
12608 backslash = 0;
12609 if (c != '\n')
12610 goto put;
12611 continue;
12612 }
12613 if (!rflag && c == '\\') {
12614 backslash++;
12615 continue;
12616 }
12617 if (c == '\n')
12618 break;
12619 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12620 continue;
12621 }
12622 startword = 0;
12623 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12624 STACKSTRNUL(p);
12625 setvar(*ap, stackblock(), 0);
12626 ap++;
12627 startword = 1;
12628 STARTSTACKSTR(p);
12629 } else {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012630 put:
Eric Andersenc470f442003-07-28 09:56:35 +000012631 STPUTC(c, p);
12632 }
12633 }
Denis Vlasenko037576d2007-10-20 18:30:38 +000012634/* end of do {} while: */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012635#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012636 while (--nchars);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012637#else
12638 while (1);
12639#endif
12640
12641#if ENABLE_ASH_READ_NCHARS
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012642 tcsetattr(fd, TCSANOW, &old_tty);
Paul Fox02eb9342005-09-07 16:56:02 +000012643#endif
12644
Eric Andersenc470f442003-07-28 09:56:35 +000012645 STACKSTRNUL(p);
12646 /* Remove trailing blanks */
12647 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12648 *p = '\0';
12649 setvar(*ap, stackblock(), 0);
12650 while (*++ap != NULL)
12651 setvar(*ap, nullstr, 0);
12652 return status;
12653}
12654
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012655static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012656umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012657{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012658 static const char permuser[3] ALIGN1 = "ugo";
12659 static const char permmode[3] ALIGN1 = "rwx";
12660 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012661 S_IRUSR, S_IWUSR, S_IXUSR,
12662 S_IRGRP, S_IWGRP, S_IXGRP,
12663 S_IROTH, S_IWOTH, S_IXOTH
12664 };
12665
12666 char *ap;
12667 mode_t mask;
12668 int i;
12669 int symbolic_mode = 0;
12670
12671 while (nextopt("S") != '\0') {
12672 symbolic_mode = 1;
12673 }
12674
Denis Vlasenkob012b102007-02-19 22:43:01 +000012675 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012676 mask = umask(0);
12677 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012678 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012679
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012680 ap = *argptr;
12681 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012682 if (symbolic_mode) {
12683 char buf[18];
12684 char *p = buf;
12685
12686 for (i = 0; i < 3; i++) {
12687 int j;
12688
12689 *p++ = permuser[i];
12690 *p++ = '=';
12691 for (j = 0; j < 3; j++) {
12692 if ((mask & permmask[3 * i + j]) == 0) {
12693 *p++ = permmode[j];
12694 }
12695 }
12696 *p++ = ',';
12697 }
12698 *--p = 0;
12699 puts(buf);
12700 } else {
12701 out1fmt("%.4o\n", mask);
12702 }
12703 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012704 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012705 mask = 0;
12706 do {
12707 if (*ap >= '8' || *ap < '0')
Denis Vlasenkob012b102007-02-19 22:43:01 +000012708 ash_msg_and_raise_error(illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012709 mask = (mask << 3) + (*ap - '0');
12710 } while (*++ap != '\0');
12711 umask(mask);
12712 } else {
12713 mask = ~mask & 0777;
12714 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012715 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012716 }
12717 umask(~mask & 0777);
12718 }
12719 }
12720 return 0;
12721}
12722
12723/*
12724 * ulimit builtin
12725 *
12726 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12727 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12728 * ash by J.T. Conklin.
12729 *
12730 * Public domain.
12731 */
12732
12733struct limits {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012734 uint8_t cmd; /* RLIMIT_xxx fit into it */
12735 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
Eric Andersenc470f442003-07-28 09:56:35 +000012736 char option;
12737};
12738
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012739static const struct limits limits_tbl[] = {
Eric Andersenc470f442003-07-28 09:56:35 +000012740#ifdef RLIMIT_CPU
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012741 { RLIMIT_CPU, 0, 't' },
Eric Andersenc470f442003-07-28 09:56:35 +000012742#endif
12743#ifdef RLIMIT_FSIZE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012744 { RLIMIT_FSIZE, 9, 'f' },
Eric Andersenc470f442003-07-28 09:56:35 +000012745#endif
12746#ifdef RLIMIT_DATA
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012747 { RLIMIT_DATA, 10, 'd' },
Eric Andersenc470f442003-07-28 09:56:35 +000012748#endif
12749#ifdef RLIMIT_STACK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012750 { RLIMIT_STACK, 10, 's' },
Eric Andersenc470f442003-07-28 09:56:35 +000012751#endif
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012752#ifdef RLIMIT_CORE
12753 { RLIMIT_CORE, 9, 'c' },
Eric Andersenc470f442003-07-28 09:56:35 +000012754#endif
12755#ifdef RLIMIT_RSS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012756 { RLIMIT_RSS, 10, 'm' },
Eric Andersenc470f442003-07-28 09:56:35 +000012757#endif
12758#ifdef RLIMIT_MEMLOCK
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012759 { RLIMIT_MEMLOCK, 10, 'l' },
Eric Andersenc470f442003-07-28 09:56:35 +000012760#endif
12761#ifdef RLIMIT_NPROC
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012762 { RLIMIT_NPROC, 0, 'p' },
Eric Andersenc470f442003-07-28 09:56:35 +000012763#endif
12764#ifdef RLIMIT_NOFILE
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012765 { RLIMIT_NOFILE, 0, 'n' },
Eric Andersenc470f442003-07-28 09:56:35 +000012766#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012767#ifdef RLIMIT_AS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012768 { RLIMIT_AS, 10, 'v' },
Eric Andersenc470f442003-07-28 09:56:35 +000012769#endif
Glenn L McGrath76620622004-01-13 10:19:37 +000012770#ifdef RLIMIT_LOCKS
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012771 { RLIMIT_LOCKS, 0, 'w' },
Eric Andersenc470f442003-07-28 09:56:35 +000012772#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012773};
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012774static const char limits_name[] =
12775#ifdef RLIMIT_CPU
12776 "time(seconds)" "\0"
12777#endif
12778#ifdef RLIMIT_FSIZE
12779 "file(blocks)" "\0"
12780#endif
12781#ifdef RLIMIT_DATA
12782 "data(kb)" "\0"
12783#endif
12784#ifdef RLIMIT_STACK
12785 "stack(kb)" "\0"
12786#endif
12787#ifdef RLIMIT_CORE
12788 "coredump(blocks)" "\0"
12789#endif
12790#ifdef RLIMIT_RSS
12791 "memory(kb)" "\0"
12792#endif
12793#ifdef RLIMIT_MEMLOCK
12794 "locked memory(kb)" "\0"
12795#endif
12796#ifdef RLIMIT_NPROC
12797 "process" "\0"
12798#endif
12799#ifdef RLIMIT_NOFILE
12800 "nofiles" "\0"
12801#endif
12802#ifdef RLIMIT_AS
12803 "vmemory(kb)" "\0"
12804#endif
12805#ifdef RLIMIT_LOCKS
12806 "locks" "\0"
12807#endif
12808;
Eric Andersenc470f442003-07-28 09:56:35 +000012809
Glenn L McGrath76620622004-01-13 10:19:37 +000012810enum limtype { SOFT = 0x1, HARD = 0x2 };
12811
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012812static void
12813printlim(enum limtype how, const struct rlimit *limit,
Glenn L McGrath76620622004-01-13 10:19:37 +000012814 const struct limits *l)
12815{
12816 rlim_t val;
12817
12818 val = limit->rlim_max;
12819 if (how & SOFT)
12820 val = limit->rlim_cur;
12821
12822 if (val == RLIM_INFINITY)
12823 out1fmt("unlimited\n");
12824 else {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012825 val >>= l->factor_shift;
Glenn L McGrath76620622004-01-13 10:19:37 +000012826 out1fmt("%lld\n", (long long) val);
12827 }
12828}
12829
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012830static int
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012831ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012832{
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012833 int c;
Eric Andersenc470f442003-07-28 09:56:35 +000012834 rlim_t val = 0;
Glenn L McGrath76620622004-01-13 10:19:37 +000012835 enum limtype how = SOFT | HARD;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012836 const struct limits *l;
12837 int set, all = 0;
12838 int optc, what;
12839 struct rlimit limit;
Eric Andersenc470f442003-07-28 09:56:35 +000012840
12841 what = 'f';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000012842 while ((optc = nextopt("HSa"
12843#ifdef RLIMIT_CPU
12844 "t"
12845#endif
12846#ifdef RLIMIT_FSIZE
12847 "f"
12848#endif
12849#ifdef RLIMIT_DATA
12850 "d"
12851#endif
12852#ifdef RLIMIT_STACK
12853 "s"
12854#endif
12855#ifdef RLIMIT_CORE
12856 "c"
12857#endif
12858#ifdef RLIMIT_RSS
12859 "m"
12860#endif
12861#ifdef RLIMIT_MEMLOCK
12862 "l"
12863#endif
12864#ifdef RLIMIT_NPROC
12865 "p"
12866#endif
12867#ifdef RLIMIT_NOFILE
12868 "n"
12869#endif
12870#ifdef RLIMIT_AS
12871 "v"
12872#endif
12873#ifdef RLIMIT_LOCKS
12874 "w"
12875#endif
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012876 )) != '\0')
Eric Andersenc470f442003-07-28 09:56:35 +000012877 switch (optc) {
12878 case 'H':
12879 how = HARD;
12880 break;
12881 case 'S':
12882 how = SOFT;
12883 break;
12884 case 'a':
12885 all = 1;
12886 break;
12887 default:
12888 what = optc;
12889 }
12890
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012891 for (l = limits_tbl; l->option != what; l++)
12892 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000012893
12894 set = *argptr ? 1 : 0;
12895 if (set) {
12896 char *p = *argptr;
12897
12898 if (all || argptr[1])
Denis Vlasenkob012b102007-02-19 22:43:01 +000012899 ash_msg_and_raise_error("too many arguments");
Eric Andersen81fe1232003-07-29 06:38:40 +000012900 if (strncmp(p, "unlimited\n", 9) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +000012901 val = RLIM_INFINITY;
12902 else {
12903 val = (rlim_t) 0;
12904
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012905 while ((c = *p++) >= '0' && c <= '9') {
Eric Andersenc470f442003-07-28 09:56:35 +000012906 val = (val * 10) + (long)(c - '0');
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012907 // val is actually 'unsigned long int' and can't get < 0
Eric Andersenc470f442003-07-28 09:56:35 +000012908 if (val < (rlim_t) 0)
12909 break;
12910 }
12911 if (c)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012912 ash_msg_and_raise_error("bad number");
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012913 val <<= l->factor_shift;
Eric Andersenc470f442003-07-28 09:56:35 +000012914 }
12915 }
12916 if (all) {
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012917 const char *lname = limits_name;
12918 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012919 getrlimit(l->cmd, &limit);
Denis Vlasenkoa59f4352007-10-29 19:17:29 +000012920 out1fmt("%-20s ", lname);
12921 lname += strlen(lname) + 1;
Glenn L McGrath76620622004-01-13 10:19:37 +000012922 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012923 }
12924 return 0;
12925 }
12926
12927 getrlimit(l->cmd, &limit);
12928 if (set) {
12929 if (how & HARD)
12930 limit.rlim_max = val;
12931 if (how & SOFT)
12932 limit.rlim_cur = val;
12933 if (setrlimit(l->cmd, &limit) < 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012934 ash_msg_and_raise_error("error setting limit (%m)");
Eric Andersenc470f442003-07-28 09:56:35 +000012935 } else {
Glenn L McGrath76620622004-01-13 10:19:37 +000012936 printlim(how, &limit, l);
Eric Andersenc470f442003-07-28 09:56:35 +000012937 }
12938 return 0;
12939}
12940
Eric Andersen90898442003-08-06 11:20:52 +000012941
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012942/* ============ Math support */
12943
Denis Vlasenko131ae172007-02-18 13:00:19 +000012944#if ENABLE_ASH_MATH_SUPPORT
Eric Andersen90898442003-08-06 11:20:52 +000012945
12946/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12947
12948 Permission is hereby granted, free of charge, to any person obtaining
12949 a copy of this software and associated documentation files (the
12950 "Software"), to deal in the Software without restriction, including
12951 without limitation the rights to use, copy, modify, merge, publish,
12952 distribute, sublicense, and/or sell copies of the Software, and to
12953 permit persons to whom the Software is furnished to do so, subject to
12954 the following conditions:
12955
12956 The above copyright notice and this permission notice shall be
12957 included in all copies or substantial portions of the Software.
12958
12959 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12960 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12961 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12962 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12963 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12964 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12965 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12966*/
12967
12968/* This is my infix parser/evaluator. It is optimized for size, intended
12969 * as a replacement for yacc-based parsers. However, it may well be faster
Eric Andersenaff114c2004-04-14 17:51:38 +000012970 * than a comparable parser written in yacc. The supported operators are
Eric Andersen90898442003-08-06 11:20:52 +000012971 * listed in #defines below. Parens, order of operations, and error handling
Eric Andersenaff114c2004-04-14 17:51:38 +000012972 * are supported. This code is thread safe. The exact expression format should
Eric Andersen90898442003-08-06 11:20:52 +000012973 * be that which POSIX specifies for shells. */
12974
12975/* The code uses a simple two-stack algorithm. See
12976 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
Eric Andersenaff114c2004-04-14 17:51:38 +000012977 * for a detailed explanation of the infix-to-postfix algorithm on which
Eric Andersen90898442003-08-06 11:20:52 +000012978 * this is based (this code differs in that it applies operators immediately
12979 * to the stack instead of adding them to a queue to end up with an
12980 * expression). */
12981
12982/* To use the routine, call it with an expression string and error return
12983 * pointer */
12984
12985/*
12986 * Aug 24, 2001 Manuel Novoa III
12987 *
12988 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12989 *
12990 * 1) In arith_apply():
12991 * a) Cached values of *numptr and &(numptr[-1]).
12992 * b) Removed redundant test for zero denominator.
12993 *
12994 * 2) In arith():
12995 * a) Eliminated redundant code for processing operator tokens by moving
12996 * to a table-based implementation. Also folded handling of parens
12997 * into the table.
12998 * b) Combined all 3 loops which called arith_apply to reduce generated
12999 * code size at the cost of speed.
13000 *
13001 * 3) The following expressions were treated as valid by the original code:
13002 * 1() , 0! , 1 ( *3 ) .
13003 * These bugs have been fixed by internally enclosing the expression in
13004 * parens and then checking that all binary ops and right parens are
13005 * preceded by a valid expression (NUM_TOKEN).
13006 *
Eric Andersenaff114c2004-04-14 17:51:38 +000013007 * Note: It may be desirable to replace Aaron's test for whitespace with
Eric Andersen90898442003-08-06 11:20:52 +000013008 * ctype's isspace() if it is used by another busybox applet or if additional
13009 * whitespace chars should be considered. Look below the "#include"s for a
13010 * precompiler test.
13011 */
13012
13013/*
13014 * Aug 26, 2001 Manuel Novoa III
13015 *
13016 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
13017 *
13018 * Merge in Aaron's comments previously posted to the busybox list,
13019 * modified slightly to take account of my changes to the code.
13020 *
13021 */
13022
13023/*
13024 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
13025 *
13026 * - allow access to variable,
13027 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
13028 * - realize assign syntax (VAR=expr, +=, *= etc)
13029 * - realize exponentiation (** operator)
13030 * - realize comma separated - expr, expr
13031 * - realise ++expr --expr expr++ expr--
13032 * - realise expr ? expr : expr (but, second expr calculate always)
Eric Andersenaff114c2004-04-14 17:51:38 +000013033 * - allow hexadecimal and octal numbers
Eric Andersen90898442003-08-06 11:20:52 +000013034 * - was restored loses XOR operator
13035 * - remove one goto label, added three ;-)
13036 * - protect $((num num)) as true zero expr (Manuel`s error)
13037 * - always use special isspace(), see comment from bash ;-)
13038 */
13039
Eric Andersen90898442003-08-06 11:20:52 +000013040#define arith_isspace(arithval) \
13041 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13042
Eric Andersen90898442003-08-06 11:20:52 +000013043typedef unsigned char operator;
13044
13045/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
Eric Andersenaff114c2004-04-14 17:51:38 +000013046 * precedence, and 3 high bits are an ID unique across operators of that
Eric Andersen90898442003-08-06 11:20:52 +000013047 * precedence. The ID portion is so that multiple operators can have the
13048 * same precedence, ensuring that the leftmost one is evaluated first.
13049 * Consider * and /. */
13050
13051#define tok_decl(prec,id) (((id)<<5)|(prec))
13052#define PREC(op) ((op) & 0x1F)
13053
13054#define TOK_LPAREN tok_decl(0,0)
13055
13056#define TOK_COMMA tok_decl(1,0)
13057
13058#define TOK_ASSIGN tok_decl(2,0)
13059#define TOK_AND_ASSIGN tok_decl(2,1)
13060#define TOK_OR_ASSIGN tok_decl(2,2)
13061#define TOK_XOR_ASSIGN tok_decl(2,3)
13062#define TOK_PLUS_ASSIGN tok_decl(2,4)
13063#define TOK_MINUS_ASSIGN tok_decl(2,5)
13064#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13065#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13066
13067#define TOK_MUL_ASSIGN tok_decl(3,0)
13068#define TOK_DIV_ASSIGN tok_decl(3,1)
13069#define TOK_REM_ASSIGN tok_decl(3,2)
13070
13071/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013072#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
Eric Andersen90898442003-08-06 11:20:52 +000013073
13074/* conditional is right associativity too */
13075#define TOK_CONDITIONAL tok_decl(4,0)
13076#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13077
13078#define TOK_OR tok_decl(5,0)
13079
13080#define TOK_AND tok_decl(6,0)
13081
13082#define TOK_BOR tok_decl(7,0)
13083
13084#define TOK_BXOR tok_decl(8,0)
13085
13086#define TOK_BAND tok_decl(9,0)
13087
13088#define TOK_EQ tok_decl(10,0)
13089#define TOK_NE tok_decl(10,1)
13090
13091#define TOK_LT tok_decl(11,0)
13092#define TOK_GT tok_decl(11,1)
13093#define TOK_GE tok_decl(11,2)
13094#define TOK_LE tok_decl(11,3)
13095
13096#define TOK_LSHIFT tok_decl(12,0)
13097#define TOK_RSHIFT tok_decl(12,1)
13098
13099#define TOK_ADD tok_decl(13,0)
13100#define TOK_SUB tok_decl(13,1)
13101
13102#define TOK_MUL tok_decl(14,0)
13103#define TOK_DIV tok_decl(14,1)
13104#define TOK_REM tok_decl(14,2)
13105
13106/* exponent is right associativity */
13107#define TOK_EXPONENT tok_decl(15,1)
13108
13109/* For now unary operators. */
13110#define UNARYPREC 16
13111#define TOK_BNOT tok_decl(UNARYPREC,0)
13112#define TOK_NOT tok_decl(UNARYPREC,1)
13113
13114#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13115#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13116
13117#define PREC_PRE (UNARYPREC+2)
13118
13119#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13120#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13121
13122#define PREC_POST (UNARYPREC+3)
13123
13124#define TOK_POST_INC tok_decl(PREC_POST, 0)
13125#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13126
13127#define SPEC_PREC (UNARYPREC+4)
13128
13129#define TOK_NUM tok_decl(SPEC_PREC, 0)
13130#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13131
13132#define NUMPTR (*numstackptr)
13133
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013134static int
13135tok_have_assign(operator op)
Eric Andersen90898442003-08-06 11:20:52 +000013136{
13137 operator prec = PREC(op);
13138
13139 convert_prec_is_assing(prec);
13140 return (prec == PREC(TOK_ASSIGN) ||
13141 prec == PREC_PRE || prec == PREC_POST);
13142}
13143
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013144static int
13145is_right_associativity(operator prec)
Eric Andersen90898442003-08-06 11:20:52 +000013146{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013147 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13148 || prec == PREC(TOK_CONDITIONAL));
Eric Andersen90898442003-08-06 11:20:52 +000013149}
13150
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013151typedef struct {
Eric Andersened9ecf72004-06-22 08:29:45 +000013152 arith_t val;
13153 arith_t contidional_second_val;
Eric Andersen90898442003-08-06 11:20:52 +000013154 char contidional_second_val_initialized;
13155 char *var; /* if NULL then is regular number,
Eric Andersenaff114c2004-04-14 17:51:38 +000013156 else is variable name */
Eric Andersen90898442003-08-06 11:20:52 +000013157} v_n_t;
13158
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013159typedef struct chk_var_recursive_looped_t {
Eric Andersen90898442003-08-06 11:20:52 +000013160 const char *var;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013161 struct chk_var_recursive_looped_t *next;
Eric Andersen90898442003-08-06 11:20:52 +000013162} chk_var_recursive_looped_t;
13163
13164static chk_var_recursive_looped_t *prev_chk_var_recursive;
13165
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013166static int
13167arith_lookup_val(v_n_t *t)
Eric Andersen90898442003-08-06 11:20:52 +000013168{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013169 if (t->var) {
13170 const char * p = lookupvar(t->var);
Eric Andersen90898442003-08-06 11:20:52 +000013171
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013172 if (p) {
13173 int errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013174
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013175 /* recursive try as expression */
13176 chk_var_recursive_looped_t *cur;
13177 chk_var_recursive_looped_t cur_save;
Eric Andersen90898442003-08-06 11:20:52 +000013178
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013179 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13180 if (strcmp(cur->var, t->var) == 0) {
13181 /* expression recursion loop detected */
13182 return -5;
13183 }
13184 }
13185 /* save current lookuped var name */
13186 cur = prev_chk_var_recursive;
13187 cur_save.var = t->var;
13188 cur_save.next = cur;
13189 prev_chk_var_recursive = &cur_save;
13190
13191 t->val = arith (p, &errcode);
13192 /* restore previous ptr after recursiving */
13193 prev_chk_var_recursive = cur;
13194 return errcode;
Eric Andersen90898442003-08-06 11:20:52 +000013195 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013196 /* allow undefined var as 0 */
13197 t->val = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013198 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013199 return 0;
Eric Andersen90898442003-08-06 11:20:52 +000013200}
13201
13202/* "applying" a token means performing it on the top elements on the integer
13203 * stack. For a unary operator it will only change the top element, but a
13204 * binary operator will pop two arguments and push a result */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013205static int
13206arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
Eric Andersen90898442003-08-06 11:20:52 +000013207{
Eric Andersen90898442003-08-06 11:20:52 +000013208 v_n_t *numptr_m1;
Eric Andersenfac312d2004-06-22 20:09:40 +000013209 arith_t numptr_val, rez;
Eric Andersen90898442003-08-06 11:20:52 +000013210 int ret_arith_lookup_val;
13211
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013212 /* There is no operator that can work without arguments */
13213 if (NUMPTR == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013214 numptr_m1 = NUMPTR - 1;
13215
13216 /* check operand is var with noninteger value */
13217 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013218 if (ret_arith_lookup_val)
Eric Andersen90898442003-08-06 11:20:52 +000013219 return ret_arith_lookup_val;
13220
13221 rez = numptr_m1->val;
13222 if (op == TOK_UMINUS)
13223 rez *= -1;
13224 else if (op == TOK_NOT)
13225 rez = !rez;
13226 else if (op == TOK_BNOT)
13227 rez = ~rez;
13228 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13229 rez++;
13230 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13231 rez--;
13232 else if (op != TOK_UPLUS) {
13233 /* Binary operators */
13234
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013235 /* check and binary operators need two arguments */
13236 if (numptr_m1 == numstack) goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013237
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013238 /* ... and they pop one */
13239 --NUMPTR;
13240 numptr_val = rez;
13241 if (op == TOK_CONDITIONAL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013242 if (!numptr_m1->contidional_second_val_initialized) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013243 /* protect $((expr1 ? expr2)) without ": expr" */
13244 goto err;
13245 }
13246 rez = numptr_m1->contidional_second_val;
13247 } else if (numptr_m1->contidional_second_val_initialized) {
13248 /* protect $((expr1 : expr2)) without "expr ? " */
13249 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013250 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013251 numptr_m1 = NUMPTR - 1;
13252 if (op != TOK_ASSIGN) {
13253 /* check operand is var with noninteger value for not '=' */
13254 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13255 if (ret_arith_lookup_val)
13256 return ret_arith_lookup_val;
13257 }
13258 if (op == TOK_CONDITIONAL) {
13259 numptr_m1->contidional_second_val = rez;
13260 }
13261 rez = numptr_m1->val;
13262 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013263 rez |= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013264 else if (op == TOK_OR)
Eric Andersen90898442003-08-06 11:20:52 +000013265 rez = numptr_val || rez;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013266 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013267 rez &= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013268 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013269 rez ^= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013270 else if (op == TOK_AND)
Eric Andersen90898442003-08-06 11:20:52 +000013271 rez = rez && numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013272 else if (op == TOK_EQ)
Eric Andersen90898442003-08-06 11:20:52 +000013273 rez = (rez == numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013274 else if (op == TOK_NE)
Eric Andersen90898442003-08-06 11:20:52 +000013275 rez = (rez != numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013276 else if (op == TOK_GE)
Eric Andersen90898442003-08-06 11:20:52 +000013277 rez = (rez >= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013278 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013279 rez >>= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013280 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013281 rez <<= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013282 else if (op == TOK_GT)
Eric Andersen90898442003-08-06 11:20:52 +000013283 rez = (rez > numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013284 else if (op == TOK_LT)
Eric Andersen90898442003-08-06 11:20:52 +000013285 rez = (rez < numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013286 else if (op == TOK_LE)
Eric Andersen90898442003-08-06 11:20:52 +000013287 rez = (rez <= numptr_val);
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013288 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013289 rez *= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013290 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013291 rez += numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013292 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013293 rez -= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013294 else if (op == TOK_ASSIGN || op == TOK_COMMA)
Eric Andersen90898442003-08-06 11:20:52 +000013295 rez = numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013296 else if (op == TOK_CONDITIONAL_SEP) {
Eric Andersen90898442003-08-06 11:20:52 +000013297 if (numptr_m1 == numstack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013298 /* protect $((expr : expr)) without "expr ? " */
13299 goto err;
Eric Andersen90898442003-08-06 11:20:52 +000013300 }
13301 numptr_m1->contidional_second_val_initialized = op;
13302 numptr_m1->contidional_second_val = numptr_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013303 } else if (op == TOK_CONDITIONAL) {
Eric Andersen90898442003-08-06 11:20:52 +000013304 rez = rez ?
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013305 numptr_val : numptr_m1->contidional_second_val;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013306 } else if (op == TOK_EXPONENT) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013307 if (numptr_val < 0)
Eric Andersen90898442003-08-06 11:20:52 +000013308 return -3; /* exponent less than 0 */
13309 else {
Eric Andersenad63cb22004-10-08 09:43:34 +000013310 arith_t c = 1;
Eric Andersen90898442003-08-06 11:20:52 +000013311
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013312 if (numptr_val)
13313 while (numptr_val--)
Eric Andersen90898442003-08-06 11:20:52 +000013314 c *= rez;
13315 rez = c;
13316 }
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013317 } else if (numptr_val==0) /* zero divisor check */
Eric Andersen90898442003-08-06 11:20:52 +000013318 return -2;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013319 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013320 rez /= numptr_val;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013321 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
Eric Andersen90898442003-08-06 11:20:52 +000013322 rez %= numptr_val;
13323 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013324 if (tok_have_assign(op)) {
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013325 char buf[sizeof(arith_t_type)*3 + 2];
Eric Andersen90898442003-08-06 11:20:52 +000013326
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013327 if (numptr_m1->var == NULL) {
Eric Andersen90898442003-08-06 11:20:52 +000013328 /* Hmm, 1=2 ? */
13329 goto err;
13330 }
13331 /* save to shell variable */
Denis Vlasenko131ae172007-02-18 13:00:19 +000013332#if ENABLE_ASH_MATH_SUPPORT_64
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013333 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013334#else
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013335 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013336#endif
Eric Andersen90898442003-08-06 11:20:52 +000013337 setvar(numptr_m1->var, buf, 0);
13338 /* after saving, make previous value for v++ or v-- */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013339 if (op == TOK_POST_INC)
Eric Andersen90898442003-08-06 11:20:52 +000013340 rez--;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013341 else if (op == TOK_POST_DEC)
Eric Andersen90898442003-08-06 11:20:52 +000013342 rez++;
13343 }
13344 numptr_m1->val = rez;
13345 /* protect geting var value, is number now */
13346 numptr_m1->var = NULL;
13347 return 0;
Denis Vlasenko079f8af2006-11-27 16:49:31 +000013348 err:
13349 return -1;
Eric Andersen90898442003-08-06 11:20:52 +000013350}
13351
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013352/* longest must be first */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013353static const char op_tokens[] ALIGN1 = {
Eric Andersen90898442003-08-06 11:20:52 +000013354 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13355 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13356 '<','<', 0, TOK_LSHIFT,
13357 '>','>', 0, TOK_RSHIFT,
13358 '|','|', 0, TOK_OR,
13359 '&','&', 0, TOK_AND,
13360 '!','=', 0, TOK_NE,
13361 '<','=', 0, TOK_LE,
13362 '>','=', 0, TOK_GE,
13363 '=','=', 0, TOK_EQ,
13364 '|','=', 0, TOK_OR_ASSIGN,
13365 '&','=', 0, TOK_AND_ASSIGN,
13366 '*','=', 0, TOK_MUL_ASSIGN,
13367 '/','=', 0, TOK_DIV_ASSIGN,
13368 '%','=', 0, TOK_REM_ASSIGN,
13369 '+','=', 0, TOK_PLUS_ASSIGN,
13370 '-','=', 0, TOK_MINUS_ASSIGN,
13371 '-','-', 0, TOK_POST_DEC,
13372 '^','=', 0, TOK_XOR_ASSIGN,
13373 '+','+', 0, TOK_POST_INC,
13374 '*','*', 0, TOK_EXPONENT,
13375 '!', 0, TOK_NOT,
13376 '<', 0, TOK_LT,
13377 '>', 0, TOK_GT,
13378 '=', 0, TOK_ASSIGN,
13379 '|', 0, TOK_BOR,
13380 '&', 0, TOK_BAND,
13381 '*', 0, TOK_MUL,
13382 '/', 0, TOK_DIV,
13383 '%', 0, TOK_REM,
13384 '+', 0, TOK_ADD,
13385 '-', 0, TOK_SUB,
13386 '^', 0, TOK_BXOR,
13387 /* uniq */
13388 '~', 0, TOK_BNOT,
13389 ',', 0, TOK_COMMA,
13390 '?', 0, TOK_CONDITIONAL,
13391 ':', 0, TOK_CONDITIONAL_SEP,
13392 ')', 0, TOK_RPAREN,
13393 '(', 0, TOK_LPAREN,
13394 0
13395};
13396/* ptr to ")" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000013397#define endexpression (&op_tokens[sizeof(op_tokens)-7])
Eric Andersen90898442003-08-06 11:20:52 +000013398
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013399static arith_t
13400arith(const char *expr, int *perrcode)
Eric Andersen90898442003-08-06 11:20:52 +000013401{
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013402 char arithval; /* Current character under analysis */
13403 operator lasttok, op;
13404 operator prec;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013405 operator *stack, *stackptr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013406 const char *p = endexpression;
13407 int errcode;
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013408 v_n_t *numstack, *numstackptr;
13409 unsigned datasizes = strlen(expr) + 2;
Eric Andersen90898442003-08-06 11:20:52 +000013410
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013411 /* Stack of integers */
13412 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13413 * in any given correct or incorrect expression is left as an exercise to
13414 * the reader. */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013415 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013416 /* Stack of operator tokens */
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013417 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
Eric Andersen90898442003-08-06 11:20:52 +000013418
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013419 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13420 *perrcode = errcode = 0;
Eric Andersen90898442003-08-06 11:20:52 +000013421
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013422 while (1) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013423 arithval = *expr;
13424 if (arithval == 0) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013425 if (p == endexpression) {
13426 /* Null expression. */
13427 return 0;
13428 }
13429
13430 /* This is only reached after all tokens have been extracted from the
13431 * input stream. If there are still tokens on the operator stack, they
13432 * are to be applied in order. At the end, there should be a final
13433 * result on the integer stack */
13434
13435 if (expr != endexpression + 1) {
13436 /* If we haven't done so already, */
13437 /* append a closing right paren */
13438 expr = endexpression;
13439 /* and let the loop process it. */
13440 continue;
13441 }
13442 /* At this point, we're done with the expression. */
13443 if (numstackptr != numstack+1) {
13444 /* ... but if there isn't, it's bad */
13445 err:
Denis Vlasenko2b75a942008-06-23 13:06:34 +000013446 *perrcode = -1;
13447 return *perrcode;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013448 }
13449 if (numstack->var) {
13450 /* expression is $((var)) only, lookup now */
13451 errcode = arith_lookup_val(numstack);
13452 }
13453 ret:
13454 *perrcode = errcode;
13455 return numstack->val;
Eric Andersen90898442003-08-06 11:20:52 +000013456 }
13457
Eric Andersen90898442003-08-06 11:20:52 +000013458 /* Continue processing the expression. */
13459 if (arith_isspace(arithval)) {
13460 /* Skip whitespace */
13461 goto prologue;
13462 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013463 p = endofname(expr);
13464 if (p != expr) {
Eric Andersenad63cb22004-10-08 09:43:34 +000013465 size_t var_name_size = (p-expr) + 1; /* trailing zero */
Eric Andersen90898442003-08-06 11:20:52 +000013466
13467 numstackptr->var = alloca(var_name_size);
13468 safe_strncpy(numstackptr->var, expr, var_name_size);
13469 expr = p;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013470 num:
Eric Andersen90898442003-08-06 11:20:52 +000013471 numstackptr->contidional_second_val_initialized = 0;
13472 numstackptr++;
13473 lasttok = TOK_NUM;
13474 continue;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013475 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013476 if (isdigit(arithval)) {
Eric Andersen90898442003-08-06 11:20:52 +000013477 numstackptr->var = NULL;
Denis Vlasenko131ae172007-02-18 13:00:19 +000013478#if ENABLE_ASH_MATH_SUPPORT_64
Eric Andersenad63cb22004-10-08 09:43:34 +000013479 numstackptr->val = strtoll(expr, (char **) &expr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013480#else
13481 numstackptr->val = strtol(expr, (char **) &expr, 0);
13482#endif
Eric Andersen90898442003-08-06 11:20:52 +000013483 goto num;
13484 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013485 for (p = op_tokens; ; p++) {
Eric Andersen90898442003-08-06 11:20:52 +000013486 const char *o;
13487
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013488 if (*p == 0) {
Eric Andersen90898442003-08-06 11:20:52 +000013489 /* strange operator not found */
13490 goto err;
13491 }
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013492 for (o = expr; *p && *o == *p; p++)
Eric Andersen90898442003-08-06 11:20:52 +000013493 o++;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000013494 if (!*p) {
Eric Andersen90898442003-08-06 11:20:52 +000013495 /* found */
13496 expr = o - 1;
13497 break;
13498 }
13499 /* skip tail uncompared token */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013500 while (*p)
Eric Andersen90898442003-08-06 11:20:52 +000013501 p++;
13502 /* skip zero delim */
13503 p++;
13504 }
13505 op = p[1];
13506
13507 /* post grammar: a++ reduce to num */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013508 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13509 lasttok = TOK_NUM;
Eric Andersen90898442003-08-06 11:20:52 +000013510
13511 /* Plus and minus are binary (not unary) _only_ if the last
13512 * token was as number, or a right paren (which pretends to be
13513 * a number, since it evaluates to one). Think about it.
13514 * It makes sense. */
13515 if (lasttok != TOK_NUM) {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000013516 switch (op) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013517 case TOK_ADD:
13518 op = TOK_UPLUS;
13519 break;
13520 case TOK_SUB:
13521 op = TOK_UMINUS;
13522 break;
13523 case TOK_POST_INC:
13524 op = TOK_PRE_INC;
13525 break;
13526 case TOK_POST_DEC:
13527 op = TOK_PRE_DEC;
13528 break;
Eric Andersen90898442003-08-06 11:20:52 +000013529 }
13530 }
13531 /* We don't want a unary operator to cause recursive descent on the
13532 * stack, because there can be many in a row and it could cause an
13533 * operator to be evaluated before its argument is pushed onto the
13534 * integer stack. */
13535 /* But for binary operators, "apply" everything on the operator
13536 * stack until we find an operator with a lesser priority than the
13537 * one we have just extracted. */
13538 /* Left paren is given the lowest priority so it will never be
13539 * "applied" in this way.
13540 * if associativity is right and priority eq, applied also skip
13541 */
13542 prec = PREC(op);
13543 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13544 /* not left paren or unary */
13545 if (lasttok != TOK_NUM) {
13546 /* binary op must be preceded by a num */
13547 goto err;
13548 }
13549 while (stackptr != stack) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013550 if (op == TOK_RPAREN) {
13551 /* The algorithm employed here is simple: while we don't
13552 * hit an open paren nor the bottom of the stack, pop
13553 * tokens and apply them */
13554 if (stackptr[-1] == TOK_LPAREN) {
13555 --stackptr;
13556 /* Any operator directly after a */
13557 lasttok = TOK_NUM;
13558 /* close paren should consider itself binary */
13559 goto prologue;
13560 }
13561 } else {
13562 operator prev_prec = PREC(stackptr[-1]);
Eric Andersen90898442003-08-06 11:20:52 +000013563
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013564 convert_prec_is_assing(prec);
13565 convert_prec_is_assing(prev_prec);
13566 if (prev_prec < prec)
13567 break;
13568 /* check right assoc */
13569 if (prev_prec == prec && is_right_associativity(prec))
13570 break;
13571 }
13572 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13573 if (errcode) goto ret;
Eric Andersen90898442003-08-06 11:20:52 +000013574 }
13575 if (op == TOK_RPAREN) {
13576 goto err;
13577 }
13578 }
13579
13580 /* Push this operator to the stack and remember it. */
13581 *stackptr++ = lasttok = op;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013582 prologue:
Eric Andersen90898442003-08-06 11:20:52 +000013583 ++expr;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000013584 } /* while */
Eric Andersen90898442003-08-06 11:20:52 +000013585}
Denis Vlasenko131ae172007-02-18 13:00:19 +000013586#endif /* ASH_MATH_SUPPORT */
Eric Andersen90898442003-08-06 11:20:52 +000013587
13588
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013589/* ============ main() and helpers */
13590
13591/*
13592 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013593 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013594static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013595static void
13596exitshell(void)
13597{
13598 struct jmploc loc;
13599 char *p;
13600 int status;
13601
13602 status = exitstatus;
13603 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
13604 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013605 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013606/* dash bug: it just does _exit(exitstatus) here
13607 * but we have to do setjobctl(0) first!
13608 * (bug is still not fixed in dash-0.5.3 - if you run dash
13609 * under Midnight Commander, on exit from dash MC is backgrounded) */
13610 status = exitstatus;
13611 goto out;
13612 }
13613 exception_handler = &loc;
13614 p = trap[0];
13615 if (p) {
13616 trap[0] = NULL;
13617 evalstring(p, 0);
13618 }
13619 flush_stdout_stderr();
13620 out:
13621 setjobctl(0);
13622 _exit(status);
13623 /* NOTREACHED */
13624}
13625
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013626static void
13627init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013628{
13629 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013630 basepf.next_to_pgetc = basepf.buf = basebuf;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013631
13632 /* from trap.c: */
13633 signal(SIGCHLD, SIG_DFL);
13634
13635 /* from var.c: */
13636 {
13637 char **envp;
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013638 char ppid[sizeof(int)*3 + 1];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013639 const char *p;
13640 struct stat st1, st2;
13641
13642 initvar();
13643 for (envp = environ; envp && *envp; envp++) {
13644 if (strchr(*envp, '=')) {
13645 setvareq(*envp, VEXPORT|VTEXTFIXED);
13646 }
13647 }
13648
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013649 snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid());
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013650 setvar("PPID", ppid, 0);
13651
13652 p = lookupvar("PWD");
13653 if (p)
13654 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
13655 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
13656 p = '\0';
13657 setpwd(p, 0);
13658 }
13659}
13660
13661/*
13662 * Process the shell command line arguments.
13663 */
13664static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013665procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013666{
13667 int i;
13668 const char *xminusc;
13669 char **xargv;
13670
13671 xargv = argv;
13672 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013673 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013674 xargv++;
13675 for (i = 0; i < NOPTS; i++)
13676 optlist[i] = 2;
13677 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013678 if (options(1)) {
13679 /* it already printed err message */
13680 raise_exception(EXERROR);
13681 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013682 xargv = argptr;
13683 xminusc = minusc;
13684 if (*xargv == NULL) {
13685 if (xminusc)
13686 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13687 sflag = 1;
13688 }
13689 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13690 iflag = 1;
13691 if (mflag == 2)
13692 mflag = iflag;
13693 for (i = 0; i < NOPTS; i++)
13694 if (optlist[i] == 2)
13695 optlist[i] = 0;
13696#if DEBUG == 2
13697 debug = 1;
13698#endif
13699 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13700 if (xminusc) {
13701 minusc = *xargv++;
13702 if (*xargv)
13703 goto setarg0;
13704 } else if (!sflag) {
13705 setinputfile(*xargv, 0);
13706 setarg0:
13707 arg0 = *xargv++;
13708 commandname = arg0;
13709 }
13710
13711 shellparam.p = xargv;
13712#if ENABLE_ASH_GETOPTS
13713 shellparam.optind = 1;
13714 shellparam.optoff = -1;
13715#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013716 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013717 while (*xargv) {
13718 shellparam.nparam++;
13719 xargv++;
13720 }
13721 optschanged();
13722}
13723
13724/*
13725 * Read /etc/profile or .profile.
13726 */
13727static void
13728read_profile(const char *name)
13729{
13730 int skip;
13731
13732 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13733 return;
13734 skip = cmdloop(0);
13735 popfile();
13736 if (skip)
13737 exitshell();
13738}
13739
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013740/*
13741 * This routine is called when an error or an interrupt occurs in an
13742 * interactive shell and control is returned to the main command loop.
13743 */
13744static void
13745reset(void)
13746{
13747 /* from eval.c: */
13748 evalskip = 0;
13749 loopnest = 0;
13750 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013751 g_parsefile->left_in_buffer = 0;
13752 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013753 popallfiles();
13754 /* from parser.c: */
13755 tokpushback = 0;
13756 checkkwd = 0;
13757 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013758 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013759}
13760
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013761#if PROFILE
13762static short profile_buf[16384];
13763extern int etext();
13764#endif
13765
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013766/*
13767 * Main routine. We initialize things, parse the arguments, execute
13768 * profiles if we're a login shell, and then call cmdloop to execute
13769 * commands. The setjmp call sets up the location to jump to when an
13770 * exception occurs. When an exception occurs the variable "state"
13771 * is used to figure out how far we had gotten.
13772 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013773int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013774int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013775{
13776 char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013777 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013778 struct jmploc jmploc;
13779 struct stackmark smark;
13780
Denis Vlasenko01631112007-12-16 17:20:38 +000013781 /* Initialize global data */
13782 INIT_G_misc();
13783 INIT_G_memstack();
13784 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013785#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013786 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013787#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013788 INIT_G_cmdtable();
13789
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013790#if PROFILE
13791 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13792#endif
13793
13794#if ENABLE_FEATURE_EDITING
13795 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13796#endif
13797 state = 0;
13798 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013799 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013800 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013801
13802 reset();
13803
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013804 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013805 if (e == EXERROR)
13806 exitstatus = 2;
13807 s = state;
13808 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
13809 exitshell();
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013810 if (e == EXINT)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013811 outcslow('\n', stderr);
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013812
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013813 popstackmark(&smark);
13814 FORCE_INT_ON; /* enable interrupts */
13815 if (s == 1)
13816 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013817 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013818 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013819 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013820 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013821 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013822 }
13823 exception_handler = &jmploc;
13824#if DEBUG
13825 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013826 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013827 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013828#endif
13829 rootpid = getpid();
13830
13831#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoce13b762008-06-29 02:25:53 +000013832 /* Can use monotonic_ns() for better randomness but for now it is
13833 * not used anywhere else in busybox... so avoid bloat */
13834 random_galois_LFSR = random_LCG = rootpid + monotonic_us();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013835#endif
13836 init();
13837 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013838 procargs(argv);
13839
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013840#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13841 if (iflag) {
13842 const char *hp = lookupvar("HISTFILE");
13843
13844 if (hp == NULL) {
13845 hp = lookupvar("HOME");
13846 if (hp != NULL) {
13847 char *defhp = concat_path_file(hp, ".ash_history");
13848 setvar("HISTFILE", defhp, 0);
13849 free(defhp);
13850 }
13851 }
13852 }
13853#endif
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013854 if (/* argv[0] && */ argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013855 isloginsh = 1;
13856 if (isloginsh) {
13857 state = 1;
13858 read_profile("/etc/profile");
13859 state1:
13860 state = 2;
13861 read_profile(".profile");
13862 }
13863 state2:
13864 state = 3;
13865 if (
13866#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013867 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013868#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013869 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013870 ) {
13871 shinit = lookupvar("ENV");
13872 if (shinit != NULL && *shinit != '\0') {
13873 read_profile(shinit);
13874 }
13875 }
13876 state3:
13877 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013878 if (minusc) {
13879 /* evalstring pushes parsefile stack.
13880 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013881 * is one of stacked source fds.
13882 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013883 if (!sflag)
13884 g_parsefile->fd = -1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013885 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013886 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013887
13888 if (sflag || minusc == NULL) {
13889#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013890 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013891 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013892 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013893 line_input_state->hist_file = hp;
13894 }
13895#endif
13896 state4: /* XXX ??? - why isn't this before the "if" statement */
13897 cmdloop(1);
13898 }
13899#if PROFILE
13900 monitor(0);
13901#endif
13902#ifdef GPROF
13903 {
13904 extern void _mcleanup(void);
13905 _mcleanup();
13906 }
13907#endif
13908 exitshell();
13909 /* NOTREACHED */
13910}
13911
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013912
Eric Andersendf82f612001-06-28 07:46:40 +000013913/*-
13914 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013915 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013916 *
13917 * This code is derived from software contributed to Berkeley by
13918 * Kenneth Almquist.
13919 *
13920 * Redistribution and use in source and binary forms, with or without
13921 * modification, are permitted provided that the following conditions
13922 * are met:
13923 * 1. Redistributions of source code must retain the above copyright
13924 * notice, this list of conditions and the following disclaimer.
13925 * 2. Redistributions in binary form must reproduce the above copyright
13926 * notice, this list of conditions and the following disclaimer in the
13927 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013928 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013929 * may be used to endorse or promote products derived from this software
13930 * without specific prior written permission.
13931 *
13932 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13933 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13934 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13935 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13936 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13937 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13938 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13939 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13940 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13941 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13942 * SUCH DAMAGE.
13943 */