blob: cccd6dd79393b749284fa9596dc59dcc0e592efa [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 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010046#if ENABLE_SH_MATH_SUPPORT
47# include "math.h"
48#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020049#if ENABLE_ASH_RANDOM_SUPPORT
50# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020051#else
52# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020053#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000054
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020055#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010056#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000057/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020058# undef CONFIG_FEATURE_SH_STANDALONE
59# undef ENABLE_FEATURE_SH_STANDALONE
60# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010061# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020062# define ENABLE_FEATURE_SH_STANDALONE 0
63# define IF_FEATURE_SH_STANDALONE(...)
64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000065#endif
66
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000068# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#endif
70
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010071#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000072# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000073#endif
74
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010075//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
Denys Vlasenko771f1992010-07-16 14:31:34 +020078
Denys Vlasenkod383b492010-09-06 10:22:13 +020079//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020080//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
81
82//config:config ASH
83//config: bool "ash"
84//config: default y
85//config: depends on !NOMMU
86//config: help
87//config: Tha 'ash' shell adds about 60k in the default configuration and is
88//config: the most complete and most pedantically correct shell included with
89//config: busybox. This shell is actually a derivative of the Debian 'dash'
90//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
91//config: (written by Kenneth Almquist) from NetBSD.
92//config:
93//config:config ASH_BASH_COMPAT
94//config: bool "bash-compatible extensions"
95//config: default y
96//config: depends on ASH
97//config: help
98//config: Enable bash-compatible extensions.
99//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100100//config:config ASH_IDLE_TIMEOUT
101//config: bool "Idle timeout variable"
102//config: default n
103//config: depends on ASH
104//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100105//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100106//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config:config ASH_JOB_CONTROL
108//config: bool "Job control"
109//config: default y
110//config: depends on ASH
111//config: help
112//config: Enable job control in the ash shell.
113//config:
114//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100115//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: default y
117//config: depends on ASH
118//config: help
119//config: Enable alias support in the ash shell.
120//config:
121//config:config ASH_GETOPTS
122//config: bool "Builtin getopt to parse positional parameters"
123//config: default y
124//config: depends on ASH
125//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100126//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200127//config:
128//config:config ASH_BUILTIN_ECHO
129//config: bool "Builtin version of 'echo'"
130//config: default y
131//config: depends on ASH
132//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100133//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config:
135//config:config ASH_BUILTIN_PRINTF
136//config: bool "Builtin version of 'printf'"
137//config: default y
138//config: depends on ASH
139//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100140//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200141//config:
142//config:config ASH_BUILTIN_TEST
143//config: bool "Builtin version of 'test'"
144//config: default y
145//config: depends on ASH
146//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100147//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200148//config:
149//config:config ASH_CMDCMD
150//config: bool "'command' command to override shell builtins"
151//config: default y
152//config: depends on ASH
153//config: help
154//config: Enable support for the ash 'command' builtin, which allows
155//config: you to run the specified command with the specified arguments,
156//config: even when there is an ash builtin command with the same name.
157//config:
158//config:config ASH_MAIL
159//config: bool "Check for new mail on interactive shells"
160//config: default n
161//config: depends on ASH
162//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100163//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200164//config:
165//config:config ASH_OPTIMIZE_FOR_SIZE
166//config: bool "Optimize for size instead of speed"
167//config: default y
168//config: depends on ASH
169//config: help
170//config: Compile ash for reduced size at the price of speed.
171//config:
172//config:config ASH_RANDOM_SUPPORT
173//config: bool "Pseudorandom generator and $RANDOM variable"
174//config: default y
175//config: depends on ASH
176//config: help
177//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
178//config: Each read of "$RANDOM" will generate a new pseudorandom value.
179//config: You can reset the generator by using a specified start value.
180//config: After "unset RANDOM" the generator will switch off and this
181//config: variable will no longer have special treatment.
182//config:
183//config:config ASH_EXPAND_PRMT
184//config: bool "Expand prompt string"
185//config: default y
186//config: depends on ASH
187//config: help
188//config: "PS#" may contain volatile content, such as backquote commands.
189//config: This option recreates the prompt string from the environment
190//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200191//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200192
193//usage:#define ash_trivial_usage NOUSAGE_STR
194//usage:#define ash_full_usage ""
195//usage:#define sh_trivial_usage NOUSAGE_STR
196//usage:#define sh_full_usage ""
197//usage:#define bash_trivial_usage NOUSAGE_STR
198//usage:#define bash_full_usage ""
199
Denis Vlasenkob012b102007-02-19 22:43:01 +0000200
Denis Vlasenko01631112007-12-16 17:20:38 +0000201/* ============ Hash table sizes. Configurable. */
202
203#define VTABSIZE 39
204#define ATABSIZE 39
205#define CMDTABLESIZE 31 /* should be prime */
206
207
Denis Vlasenkob012b102007-02-19 22:43:01 +0000208/* ============ Shell options */
209
210static const char *const optletters_optnames[] = {
211 "e" "errexit",
212 "f" "noglob",
213 "I" "ignoreeof",
214 "i" "interactive",
215 "m" "monitor",
216 "n" "noexec",
217 "s" "stdin",
218 "x" "xtrace",
219 "v" "verbose",
220 "C" "noclobber",
221 "a" "allexport",
222 "b" "notify",
223 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100224 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100225#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100226 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100227#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000228#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000229 ,"\0" "nolog"
230 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231#endif
232};
233
Denys Vlasenko285ad152009-12-04 23:02:27 +0100234#define optletters(n) optletters_optnames[n][0]
235#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000236
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000237enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000238
Eric Andersenc470f442003-07-28 09:56:35 +0000239
Denis Vlasenkob012b102007-02-19 22:43:01 +0000240/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000241
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200242#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000243
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000244/*
Eric Andersenc470f442003-07-28 09:56:35 +0000245 * We enclose jmp_buf in a structure so that we can declare pointers to
246 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000247 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000248 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000249 * exception handlers, the user should save the value of handler on entry
250 * to an inner scope, set handler to point to a jmploc structure for the
251 * inner scope, and restore handler on exit from the scope.
252 */
Eric Andersenc470f442003-07-28 09:56:35 +0000253struct jmploc {
254 jmp_buf loc;
255};
Denis Vlasenko01631112007-12-16 17:20:38 +0000256
257struct globals_misc {
258 /* pid of main shell */
259 int rootpid;
260 /* shell level: 0 for the main shell, 1 for its children, and so on */
261 int shlvl;
262#define rootshell (!shlvl)
263 char *minusc; /* argument to -c option */
264
265 char *curdir; // = nullstr; /* current working directory */
266 char *physdir; // = nullstr; /* physical working directory */
267
268 char *arg0; /* value of $0 */
269
270 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000271
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200272 volatile int suppress_int; /* counter */
273 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000274 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200275 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000276 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000277 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000278#define EXINT 0 /* SIGINT received */
279#define EXERROR 1 /* a generic error */
280#define EXSHELLPROC 2 /* execute a shell procedure */
281#define EXEXEC 3 /* command execution failed */
282#define EXEXIT 4 /* exit the shell */
283#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000284
Denis Vlasenko01631112007-12-16 17:20:38 +0000285 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000286 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000287
288 char optlist[NOPTS];
289#define eflag optlist[0]
290#define fflag optlist[1]
291#define Iflag optlist[2]
292#define iflag optlist[3]
293#define mflag optlist[4]
294#define nflag optlist[5]
295#define sflag optlist[6]
296#define xflag optlist[7]
297#define vflag optlist[8]
298#define Cflag optlist[9]
299#define aflag optlist[10]
300#define bflag optlist[11]
301#define uflag optlist[12]
302#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100303#if ENABLE_ASH_BASH_COMPAT
304# define pipefail optlist[14]
305#else
306# define pipefail 0
307#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000308#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100309# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
310# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000311#endif
312
313 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000314 /*
315 * Sigmode records the current value of the signal handlers for the various
316 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000317 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000318 */
319 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000320#define S_DFL 1 /* default signal handling (SIG_DFL) */
321#define S_CATCH 2 /* signal is caught */
322#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000323#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000324
Denis Vlasenko01631112007-12-16 17:20:38 +0000325 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000326 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200327 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000328 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200329 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000330
331 /* Rarely referenced stuff */
332#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200333 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000334#endif
335 pid_t backgndpid; /* pid of last background process */
336 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000337};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000338extern struct globals_misc *const ash_ptr_to_globals_misc;
339#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000340#define rootpid (G_misc.rootpid )
341#define shlvl (G_misc.shlvl )
342#define minusc (G_misc.minusc )
343#define curdir (G_misc.curdir )
344#define physdir (G_misc.physdir )
345#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000346#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000347#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200348#define suppress_int (G_misc.suppress_int )
349#define pending_int (G_misc.pending_int )
350#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000351#define isloginsh (G_misc.isloginsh )
352#define nullstr (G_misc.nullstr )
353#define optlist (G_misc.optlist )
354#define sigmode (G_misc.sigmode )
355#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200356#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000357#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200358#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200359#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000360#define backgndpid (G_misc.backgndpid )
361#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000362#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000363 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
364 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000365 curdir = nullstr; \
366 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200367 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000368} while (0)
369
370
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000371/* ============ DEBUG */
372#if DEBUG
373static void trace_printf(const char *fmt, ...);
374static void trace_vprintf(const char *fmt, va_list va);
375# define TRACE(param) trace_printf param
376# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000377# define close(fd) do { \
378 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000379 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200380 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000381 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000382} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000383#else
384# define TRACE(param)
385# define TRACEV(param)
386#endif
387
388
Denis Vlasenko559691a2008-10-05 18:39:31 +0000389/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000390#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
391
Denis Vlasenko559691a2008-10-05 18:39:31 +0000392static int isdigit_str9(const char *str)
393{
394 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
395 while (--maxlen && isdigit(*str))
396 str++;
397 return (*str == '\0');
398}
Denis Vlasenko01631112007-12-16 17:20:38 +0000399
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200400static const char *var_end(const char *var)
401{
402 while (*var)
403 if (*var++ == '=')
404 break;
405 return var;
406}
407
Denis Vlasenko559691a2008-10-05 18:39:31 +0000408
409/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100410
411static void exitshell(void) NORETURN;
412
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000413/*
Eric Andersen2870d962001-07-02 17:27:21 +0000414 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000415 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000416 * much more efficient and portable. (But hacking the kernel is so much
417 * more fun than worrying about efficiency and portability. :-))
418 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000419#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200420 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000421 xbarrier(); \
422} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423
424/*
425 * Called to raise an exception. Since C doesn't include exceptions, we
426 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000427 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000428 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000429static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000430static void
431raise_exception(int e)
432{
433#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000434 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000435 abort();
436#endif
437 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000438 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000439 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000440}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000441#if DEBUG
442#define raise_exception(e) do { \
443 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
444 raise_exception(e); \
445} while (0)
446#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000447
448/*
449 * Called from trap.c when a SIGINT is received. (If the user specifies
450 * that SIGINT is to be trapped or ignored using the trap builtin, then
451 * this routine is not called.) Suppressint is nonzero when interrupts
452 * are held using the INT_OFF macro. (The test for iflag is just
453 * defensive programming.)
454 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000455static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000456static void
457raise_interrupt(void)
458{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000459 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000460
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200461 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000462 /* Signal is not automatically unmasked after it is raised,
463 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000464 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200465 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000466
Denis Vlasenko4b875702009-03-19 13:30:04 +0000467 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000468 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
469 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000470 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000471 signal(SIGINT, SIG_DFL);
472 raise(SIGINT);
473 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000474 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000475 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000476 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000477 /* NOTREACHED */
478}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000479#if DEBUG
480#define raise_interrupt() do { \
481 TRACE(("raising interrupt on line %d\n", __LINE__)); \
482 raise_interrupt(); \
483} while (0)
484#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000485
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000486static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000487int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000488{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000489 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200490 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000491 raise_interrupt();
492 }
493}
494#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000495static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000496force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000497{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000498 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200499 suppress_int = 0;
500 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000501 raise_interrupt();
502}
503#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000504
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200505#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000506
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000507#define RESTORE_INT(v) do { \
508 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200509 suppress_int = (v); \
510 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000511 raise_interrupt(); \
512} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000513
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000514
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000515/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000516
Eric Andersenc470f442003-07-28 09:56:35 +0000517static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000518outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000519{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000520 INT_OFF;
521 fputs(p, file);
522 INT_ON;
523}
524
525static void
526flush_stdout_stderr(void)
527{
528 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100529 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000530 INT_ON;
531}
532
533static void
534outcslow(int c, FILE *dest)
535{
536 INT_OFF;
537 putc(c, dest);
538 fflush(dest);
539 INT_ON;
540}
541
542static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
543static int
544out1fmt(const char *fmt, ...)
545{
546 va_list ap;
547 int r;
548
549 INT_OFF;
550 va_start(ap, fmt);
551 r = vprintf(fmt, ap);
552 va_end(ap);
553 INT_ON;
554 return r;
555}
556
557static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
558static int
559fmtstr(char *outbuf, size_t length, const char *fmt, ...)
560{
561 va_list ap;
562 int ret;
563
564 va_start(ap, fmt);
565 INT_OFF;
566 ret = vsnprintf(outbuf, length, fmt, ap);
567 va_end(ap);
568 INT_ON;
569 return ret;
570}
571
572static void
573out1str(const char *p)
574{
575 outstr(p, stdout);
576}
577
578static void
579out2str(const char *p)
580{
581 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100582 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000583}
584
585
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000586/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000587
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000588/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100589#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200590#define CTLESC ((unsigned char)'\201') /* escape next character */
591#define CTLVAR ((unsigned char)'\202') /* variable defn */
592#define CTLENDVAR ((unsigned char)'\203')
593#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000594#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
595/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200596#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
597#define CTLENDARI ((unsigned char)'\207')
598#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100599#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000600
601/* variable substitution byte (follows CTLVAR) */
602#define VSTYPE 0x0f /* type of variable substitution */
603#define VSNUL 0x10 /* colon--treat the empty string as unset */
604#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
605
606/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000607#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
608#define VSMINUS 0x2 /* ${var-text} */
609#define VSPLUS 0x3 /* ${var+text} */
610#define VSQUESTION 0x4 /* ${var?message} */
611#define VSASSIGN 0x5 /* ${var=text} */
612#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
613#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
614#define VSTRIMLEFT 0x8 /* ${var#pattern} */
615#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
616#define VSLENGTH 0xa /* ${#var} */
617#if ENABLE_ASH_BASH_COMPAT
618#define VSSUBSTR 0xc /* ${var:position:length} */
619#define VSREPLACE 0xd /* ${var/pattern/replacement} */
620#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
621#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000622
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000623static const char dolatstr[] ALIGN1 = {
624 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
625};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000626
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627#define NCMD 0
628#define NPIPE 1
629#define NREDIR 2
630#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000631#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000632#define NAND 5
633#define NOR 6
634#define NSEMI 7
635#define NIF 8
636#define NWHILE 9
637#define NUNTIL 10
638#define NFOR 11
639#define NCASE 12
640#define NCLIST 13
641#define NDEFUN 14
642#define NARG 15
643#define NTO 16
644#if ENABLE_ASH_BASH_COMPAT
645#define NTO2 17
646#endif
647#define NCLOBBER 18
648#define NFROM 19
649#define NFROMTO 20
650#define NAPPEND 21
651#define NTOFD 22
652#define NFROMFD 23
653#define NHERE 24
654#define NXHERE 25
655#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000656#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000657
658union node;
659
660struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000661 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000662 union node *assign;
663 union node *args;
664 union node *redirect;
665};
666
667struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000668 smallint type;
669 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000670 struct nodelist *cmdlist;
671};
672
673struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000674 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000675 union node *n;
676 union node *redirect;
677};
678
679struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000680 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000681 union node *ch1;
682 union node *ch2;
683};
684
685struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000686 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000687 union node *test;
688 union node *ifpart;
689 union node *elsepart;
690};
691
692struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000693 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000694 union node *args;
695 union node *body;
696 char *var;
697};
698
699struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000700 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000701 union node *expr;
702 union node *cases;
703};
704
705struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000706 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000707 union node *next;
708 union node *pattern;
709 union node *body;
710};
711
712struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000713 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000714 union node *next;
715 char *text;
716 struct nodelist *backquote;
717};
718
Denis Vlasenko559691a2008-10-05 18:39:31 +0000719/* nfile and ndup layout must match!
720 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
721 * that it is actually NTO2 (>&file), and change its type.
722 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000723struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000724 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000725 union node *next;
726 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000727 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000728 union node *fname;
729 char *expfname;
730};
731
732struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000733 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000734 union node *next;
735 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000736 int dupfd;
737 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000738 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000739};
740
741struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000742 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000743 union node *next;
744 int fd;
745 union node *doc;
746};
747
748struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000749 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000750 union node *com;
751};
752
753union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000754 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000755 struct ncmd ncmd;
756 struct npipe npipe;
757 struct nredir nredir;
758 struct nbinary nbinary;
759 struct nif nif;
760 struct nfor nfor;
761 struct ncase ncase;
762 struct nclist nclist;
763 struct narg narg;
764 struct nfile nfile;
765 struct ndup ndup;
766 struct nhere nhere;
767 struct nnot nnot;
768};
769
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200770/*
771 * NODE_EOF is returned by parsecmd when it encounters an end of file.
772 * It must be distinct from NULL.
773 */
774#define NODE_EOF ((union node *) -1L)
775
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000776struct nodelist {
777 struct nodelist *next;
778 union node *n;
779};
780
781struct funcnode {
782 int count;
783 union node n;
784};
785
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000786/*
787 * Free a parse tree.
788 */
789static void
790freefunc(struct funcnode *f)
791{
792 if (f && --f->count < 0)
793 free(f);
794}
795
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000796
797/* ============ Debugging output */
798
799#if DEBUG
800
801static FILE *tracefile;
802
803static void
804trace_printf(const char *fmt, ...)
805{
806 va_list va;
807
808 if (debug != 1)
809 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000810 if (DEBUG_TIME)
811 fprintf(tracefile, "%u ", (int) time(NULL));
812 if (DEBUG_PID)
813 fprintf(tracefile, "[%u] ", (int) getpid());
814 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200815 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000816 va_start(va, fmt);
817 vfprintf(tracefile, fmt, va);
818 va_end(va);
819}
820
821static void
822trace_vprintf(const char *fmt, va_list va)
823{
824 if (debug != 1)
825 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000826 if (DEBUG_TIME)
827 fprintf(tracefile, "%u ", (int) time(NULL));
828 if (DEBUG_PID)
829 fprintf(tracefile, "[%u] ", (int) getpid());
830 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200831 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000832 vfprintf(tracefile, fmt, va);
833}
834
835static void
836trace_puts(const char *s)
837{
838 if (debug != 1)
839 return;
840 fputs(s, tracefile);
841}
842
843static void
844trace_puts_quoted(char *s)
845{
846 char *p;
847 char c;
848
849 if (debug != 1)
850 return;
851 putc('"', tracefile);
852 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100853 switch ((unsigned char)*p) {
854 case '\n': c = 'n'; goto backslash;
855 case '\t': c = 't'; goto backslash;
856 case '\r': c = 'r'; goto backslash;
857 case '\"': c = '\"'; goto backslash;
858 case '\\': c = '\\'; goto backslash;
859 case CTLESC: c = 'e'; goto backslash;
860 case CTLVAR: c = 'v'; goto backslash;
861 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
862 case CTLBACKQ: c = 'q'; goto backslash;
863 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000864 backslash:
865 putc('\\', tracefile);
866 putc(c, tracefile);
867 break;
868 default:
869 if (*p >= ' ' && *p <= '~')
870 putc(*p, tracefile);
871 else {
872 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100873 putc((*p >> 6) & 03, tracefile);
874 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000875 putc(*p & 07, tracefile);
876 }
877 break;
878 }
879 }
880 putc('"', tracefile);
881}
882
883static void
884trace_puts_args(char **ap)
885{
886 if (debug != 1)
887 return;
888 if (!*ap)
889 return;
890 while (1) {
891 trace_puts_quoted(*ap);
892 if (!*++ap) {
893 putc('\n', tracefile);
894 break;
895 }
896 putc(' ', tracefile);
897 }
898}
899
900static void
901opentrace(void)
902{
903 char s[100];
904#ifdef O_APPEND
905 int flags;
906#endif
907
908 if (debug != 1) {
909 if (tracefile)
910 fflush(tracefile);
911 /* leave open because libedit might be using it */
912 return;
913 }
914 strcpy(s, "./trace");
915 if (tracefile) {
916 if (!freopen(s, "a", tracefile)) {
917 fprintf(stderr, "Can't re-open %s\n", s);
918 debug = 0;
919 return;
920 }
921 } else {
922 tracefile = fopen(s, "a");
923 if (tracefile == NULL) {
924 fprintf(stderr, "Can't open %s\n", s);
925 debug = 0;
926 return;
927 }
928 }
929#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000930 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000931 if (flags >= 0)
932 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
933#endif
934 setlinebuf(tracefile);
935 fputs("\nTracing started.\n", tracefile);
936}
937
938static void
939indent(int amount, char *pfx, FILE *fp)
940{
941 int i;
942
943 for (i = 0; i < amount; i++) {
944 if (pfx && i == amount - 1)
945 fputs(pfx, fp);
946 putc('\t', fp);
947 }
948}
949
950/* little circular references here... */
951static void shtree(union node *n, int ind, char *pfx, FILE *fp);
952
953static void
954sharg(union node *arg, FILE *fp)
955{
956 char *p;
957 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100958 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000959
960 if (arg->type != NARG) {
961 out1fmt("<node type %d>\n", arg->type);
962 abort();
963 }
964 bqlist = arg->narg.backquote;
965 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100966 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000967 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700968 p++;
969 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000970 break;
971 case CTLVAR:
972 putc('$', fp);
973 putc('{', fp);
974 subtype = *++p;
975 if (subtype == VSLENGTH)
976 putc('#', fp);
977
Dan Fandrich77d48722010-09-07 23:38:28 -0700978 while (*p != '=') {
979 putc(*p, fp);
980 p++;
981 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000982
983 if (subtype & VSNUL)
984 putc(':', fp);
985
986 switch (subtype & VSTYPE) {
987 case VSNORMAL:
988 putc('}', fp);
989 break;
990 case VSMINUS:
991 putc('-', fp);
992 break;
993 case VSPLUS:
994 putc('+', fp);
995 break;
996 case VSQUESTION:
997 putc('?', fp);
998 break;
999 case VSASSIGN:
1000 putc('=', fp);
1001 break;
1002 case VSTRIMLEFT:
1003 putc('#', fp);
1004 break;
1005 case VSTRIMLEFTMAX:
1006 putc('#', fp);
1007 putc('#', fp);
1008 break;
1009 case VSTRIMRIGHT:
1010 putc('%', fp);
1011 break;
1012 case VSTRIMRIGHTMAX:
1013 putc('%', fp);
1014 putc('%', fp);
1015 break;
1016 case VSLENGTH:
1017 break;
1018 default:
1019 out1fmt("<subtype %d>", subtype);
1020 }
1021 break;
1022 case CTLENDVAR:
1023 putc('}', fp);
1024 break;
1025 case CTLBACKQ:
1026 case CTLBACKQ|CTLQUOTE:
1027 putc('$', fp);
1028 putc('(', fp);
1029 shtree(bqlist->n, -1, NULL, fp);
1030 putc(')', fp);
1031 break;
1032 default:
1033 putc(*p, fp);
1034 break;
1035 }
1036 }
1037}
1038
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001039static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001040shcmd(union node *cmd, FILE *fp)
1041{
1042 union node *np;
1043 int first;
1044 const char *s;
1045 int dftfd;
1046
1047 first = 1;
1048 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001049 if (!first)
1050 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001051 sharg(np, fp);
1052 first = 0;
1053 }
1054 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001055 if (!first)
1056 putc(' ', fp);
1057 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001058 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001059 case NTO: s = ">>"+1; dftfd = 1; break;
1060 case NCLOBBER: s = ">|"; dftfd = 1; break;
1061 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001062#if ENABLE_ASH_BASH_COMPAT
1063 case NTO2:
1064#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001065 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001066 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001067 case NFROMFD: s = "<&"; break;
1068 case NFROMTO: s = "<>"; break;
1069 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001070 }
1071 if (np->nfile.fd != dftfd)
1072 fprintf(fp, "%d", np->nfile.fd);
1073 fputs(s, fp);
1074 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1075 fprintf(fp, "%d", np->ndup.dupfd);
1076 } else {
1077 sharg(np->nfile.fname, fp);
1078 }
1079 first = 0;
1080 }
1081}
1082
1083static void
1084shtree(union node *n, int ind, char *pfx, FILE *fp)
1085{
1086 struct nodelist *lp;
1087 const char *s;
1088
1089 if (n == NULL)
1090 return;
1091
1092 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001093
1094 if (n == NODE_EOF) {
1095 fputs("<EOF>", fp);
1096 return;
1097 }
1098
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001099 switch (n->type) {
1100 case NSEMI:
1101 s = "; ";
1102 goto binop;
1103 case NAND:
1104 s = " && ";
1105 goto binop;
1106 case NOR:
1107 s = " || ";
1108 binop:
1109 shtree(n->nbinary.ch1, ind, NULL, fp);
1110 /* if (ind < 0) */
1111 fputs(s, fp);
1112 shtree(n->nbinary.ch2, ind, NULL, fp);
1113 break;
1114 case NCMD:
1115 shcmd(n, fp);
1116 if (ind >= 0)
1117 putc('\n', fp);
1118 break;
1119 case NPIPE:
1120 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001121 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001122 if (lp->next)
1123 fputs(" | ", fp);
1124 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001125 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001126 fputs(" &", fp);
1127 if (ind >= 0)
1128 putc('\n', fp);
1129 break;
1130 default:
1131 fprintf(fp, "<node type %d>", n->type);
1132 if (ind >= 0)
1133 putc('\n', fp);
1134 break;
1135 }
1136}
1137
1138static void
1139showtree(union node *n)
1140{
1141 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001142 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001143}
1144
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001145#endif /* DEBUG */
1146
1147
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001148/* ============ Parser data */
1149
1150/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001151 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1152 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001153struct strlist {
1154 struct strlist *next;
1155 char *text;
1156};
1157
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001158struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001159
Denis Vlasenkob012b102007-02-19 22:43:01 +00001160struct strpush {
1161 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001162 char *prev_string;
1163 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001164#if ENABLE_ASH_ALIAS
1165 struct alias *ap; /* if push was associated with an alias */
1166#endif
1167 char *string; /* remember the string since it may change */
1168};
1169
1170struct parsefile {
1171 struct parsefile *prev; /* preceding file on stack */
1172 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001173 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001174 int left_in_line; /* number of chars left in this line */
1175 int left_in_buffer; /* number of chars left in this buffer past the line */
1176 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001177 char *buf; /* input buffer */
1178 struct strpush *strpush; /* for pushing strings at this level */
1179 struct strpush basestrpush; /* so pushing one is fast */
1180};
1181
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001182static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001183static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001184static int startlinno; /* line # where last token started */
1185static char *commandname; /* currently executing command */
1186static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001187static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001188
1189
1190/* ============ Message printing */
1191
1192static void
1193ash_vmsg(const char *msg, va_list ap)
1194{
1195 fprintf(stderr, "%s: ", arg0);
1196 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001197 if (strcmp(arg0, commandname))
1198 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001199 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001200 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001201 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001202 vfprintf(stderr, msg, ap);
1203 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001204}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001205
1206/*
1207 * Exverror is called to raise the error exception. If the second argument
1208 * is not NULL then error prints an error message using printf style
1209 * formatting. It then raises the error exception.
1210 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001211static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001212static void
1213ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001214{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001215#if DEBUG
1216 if (msg) {
1217 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1218 TRACEV((msg, ap));
1219 TRACE(("\") pid=%d\n", getpid()));
1220 } else
1221 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1222 if (msg)
1223#endif
1224 ash_vmsg(msg, ap);
1225
1226 flush_stdout_stderr();
1227 raise_exception(cond);
1228 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001229}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001230
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001231static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001232static void
1233ash_msg_and_raise_error(const char *msg, ...)
1234{
1235 va_list ap;
1236
1237 va_start(ap, msg);
1238 ash_vmsg_and_raise(EXERROR, msg, ap);
1239 /* NOTREACHED */
1240 va_end(ap);
1241}
1242
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001243static void raise_error_syntax(const char *) NORETURN;
1244static void
1245raise_error_syntax(const char *msg)
1246{
1247 ash_msg_and_raise_error("syntax error: %s", msg);
1248 /* NOTREACHED */
1249}
1250
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001251static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001252static void
1253ash_msg_and_raise(int cond, const char *msg, ...)
1254{
1255 va_list ap;
1256
1257 va_start(ap, msg);
1258 ash_vmsg_and_raise(cond, msg, ap);
1259 /* NOTREACHED */
1260 va_end(ap);
1261}
1262
1263/*
1264 * error/warning routines for external builtins
1265 */
1266static void
1267ash_msg(const char *fmt, ...)
1268{
1269 va_list ap;
1270
1271 va_start(ap, fmt);
1272 ash_vmsg(fmt, ap);
1273 va_end(ap);
1274}
1275
1276/*
1277 * Return a string describing an error. The returned string may be a
1278 * pointer to a static buffer that will be overwritten on the next call.
1279 * Action describes the operation that got the error.
1280 */
1281static const char *
1282errmsg(int e, const char *em)
1283{
1284 if (e == ENOENT || e == ENOTDIR) {
1285 return em;
1286 }
1287 return strerror(e);
1288}
1289
1290
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001291/* ============ Memory allocation */
1292
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001293#if 0
1294/* I consider these wrappers nearly useless:
1295 * ok, they return you to nearest exception handler, but
1296 * how much memory do you leak in the process, making
1297 * memory starvation worse?
1298 */
1299static void *
1300ckrealloc(void * p, size_t nbytes)
1301{
1302 p = realloc(p, nbytes);
1303 if (!p)
1304 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1305 return p;
1306}
1307
1308static void *
1309ckmalloc(size_t nbytes)
1310{
1311 return ckrealloc(NULL, nbytes);
1312}
1313
1314static void *
1315ckzalloc(size_t nbytes)
1316{
1317 return memset(ckmalloc(nbytes), 0, nbytes);
1318}
1319
1320static char *
1321ckstrdup(const char *s)
1322{
1323 char *p = strdup(s);
1324 if (!p)
1325 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1326 return p;
1327}
1328#else
1329/* Using bbox equivalents. They exit if out of memory */
1330# define ckrealloc xrealloc
1331# define ckmalloc xmalloc
1332# define ckzalloc xzalloc
1333# define ckstrdup xstrdup
1334#endif
1335
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001336/*
1337 * It appears that grabstackstr() will barf with such alignments
1338 * because stalloc() will return a string allocated in a new stackblock.
1339 */
1340#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1341enum {
1342 /* Most machines require the value returned from malloc to be aligned
1343 * in some way. The following macro will get this right
1344 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001345 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001346 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001347 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001348};
1349
1350struct stack_block {
1351 struct stack_block *prev;
1352 char space[MINSIZE];
1353};
1354
1355struct stackmark {
1356 struct stack_block *stackp;
1357 char *stacknxt;
1358 size_t stacknleft;
1359 struct stackmark *marknext;
1360};
1361
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001362
Denis Vlasenko01631112007-12-16 17:20:38 +00001363struct globals_memstack {
1364 struct stack_block *g_stackp; // = &stackbase;
1365 struct stackmark *markp;
1366 char *g_stacknxt; // = stackbase.space;
1367 char *sstrend; // = stackbase.space + MINSIZE;
1368 size_t g_stacknleft; // = MINSIZE;
1369 int herefd; // = -1;
1370 struct stack_block stackbase;
1371};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001372extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1373#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001374#define g_stackp (G_memstack.g_stackp )
1375#define markp (G_memstack.markp )
1376#define g_stacknxt (G_memstack.g_stacknxt )
1377#define sstrend (G_memstack.sstrend )
1378#define g_stacknleft (G_memstack.g_stacknleft)
1379#define herefd (G_memstack.herefd )
1380#define stackbase (G_memstack.stackbase )
1381#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001382 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1383 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001384 g_stackp = &stackbase; \
1385 g_stacknxt = stackbase.space; \
1386 g_stacknleft = MINSIZE; \
1387 sstrend = stackbase.space + MINSIZE; \
1388 herefd = -1; \
1389} while (0)
1390
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001391
Denis Vlasenko01631112007-12-16 17:20:38 +00001392#define stackblock() ((void *)g_stacknxt)
1393#define stackblocksize() g_stacknleft
1394
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001395/*
1396 * Parse trees for commands are allocated in lifo order, so we use a stack
1397 * to make this more efficient, and also to avoid all sorts of exception
1398 * handling code to handle interrupts in the middle of a parse.
1399 *
1400 * The size 504 was chosen because the Ultrix malloc handles that size
1401 * well.
1402 */
1403static void *
1404stalloc(size_t nbytes)
1405{
1406 char *p;
1407 size_t aligned;
1408
1409 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001410 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001411 size_t len;
1412 size_t blocksize;
1413 struct stack_block *sp;
1414
1415 blocksize = aligned;
1416 if (blocksize < MINSIZE)
1417 blocksize = MINSIZE;
1418 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1419 if (len < blocksize)
1420 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1421 INT_OFF;
1422 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001423 sp->prev = g_stackp;
1424 g_stacknxt = sp->space;
1425 g_stacknleft = blocksize;
1426 sstrend = g_stacknxt + blocksize;
1427 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001428 INT_ON;
1429 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001430 p = g_stacknxt;
1431 g_stacknxt += aligned;
1432 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001433 return p;
1434}
1435
Denis Vlasenko597906c2008-02-20 16:38:54 +00001436static void *
1437stzalloc(size_t nbytes)
1438{
1439 return memset(stalloc(nbytes), 0, nbytes);
1440}
1441
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001442static void
1443stunalloc(void *p)
1444{
1445#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001446 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001447 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001448 abort();
1449 }
1450#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001451 g_stacknleft += g_stacknxt - (char *)p;
1452 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001453}
1454
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001455/*
1456 * Like strdup but works with the ash stack.
1457 */
1458static char *
1459ststrdup(const char *p)
1460{
1461 size_t len = strlen(p) + 1;
1462 return memcpy(stalloc(len), p, len);
1463}
1464
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001465static void
1466setstackmark(struct stackmark *mark)
1467{
Denis Vlasenko01631112007-12-16 17:20:38 +00001468 mark->stackp = g_stackp;
1469 mark->stacknxt = g_stacknxt;
1470 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001471 mark->marknext = markp;
1472 markp = mark;
1473}
1474
1475static void
1476popstackmark(struct stackmark *mark)
1477{
1478 struct stack_block *sp;
1479
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001480 if (!mark->stackp)
1481 return;
1482
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001483 INT_OFF;
1484 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001485 while (g_stackp != mark->stackp) {
1486 sp = g_stackp;
1487 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001488 free(sp);
1489 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001490 g_stacknxt = mark->stacknxt;
1491 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001492 sstrend = mark->stacknxt + mark->stacknleft;
1493 INT_ON;
1494}
1495
1496/*
1497 * When the parser reads in a string, it wants to stick the string on the
1498 * stack and only adjust the stack pointer when it knows how big the
1499 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1500 * of space on top of the stack and stackblocklen returns the length of
1501 * this block. Growstackblock will grow this space by at least one byte,
1502 * possibly moving it (like realloc). Grabstackblock actually allocates the
1503 * part of the block that has been used.
1504 */
1505static void
1506growstackblock(void)
1507{
1508 size_t newlen;
1509
Denis Vlasenko01631112007-12-16 17:20:38 +00001510 newlen = g_stacknleft * 2;
1511 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001512 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1513 if (newlen < 128)
1514 newlen += 128;
1515
Denis Vlasenko01631112007-12-16 17:20:38 +00001516 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001517 struct stack_block *oldstackp;
1518 struct stackmark *xmark;
1519 struct stack_block *sp;
1520 struct stack_block *prevstackp;
1521 size_t grosslen;
1522
1523 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001524 oldstackp = g_stackp;
1525 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001526 prevstackp = sp->prev;
1527 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1528 sp = ckrealloc(sp, grosslen);
1529 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001530 g_stackp = sp;
1531 g_stacknxt = sp->space;
1532 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001533 sstrend = sp->space + newlen;
1534
1535 /*
1536 * Stack marks pointing to the start of the old block
1537 * must be relocated to point to the new block
1538 */
1539 xmark = markp;
1540 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001541 xmark->stackp = g_stackp;
1542 xmark->stacknxt = g_stacknxt;
1543 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001544 xmark = xmark->marknext;
1545 }
1546 INT_ON;
1547 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001548 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001549 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001550 char *p = stalloc(newlen);
1551
1552 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001553 g_stacknxt = memcpy(p, oldspace, oldlen);
1554 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001555 }
1556}
1557
1558static void
1559grabstackblock(size_t len)
1560{
1561 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001562 g_stacknxt += len;
1563 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001564}
1565
1566/*
1567 * The following routines are somewhat easier to use than the above.
1568 * The user declares a variable of type STACKSTR, which may be declared
1569 * to be a register. The macro STARTSTACKSTR initializes things. Then
1570 * the user uses the macro STPUTC to add characters to the string. In
1571 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1572 * grown as necessary. When the user is done, she can just leave the
1573 * string there and refer to it using stackblock(). Or she can allocate
1574 * the space for it using grabstackstr(). If it is necessary to allow
1575 * someone else to use the stack temporarily and then continue to grow
1576 * the string, the user should use grabstack to allocate the space, and
1577 * then call ungrabstr(p) to return to the previous mode of operation.
1578 *
1579 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1580 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1581 * is space for at least one character.
1582 */
1583static void *
1584growstackstr(void)
1585{
1586 size_t len = stackblocksize();
1587 if (herefd >= 0 && len >= 1024) {
1588 full_write(herefd, stackblock(), len);
1589 return stackblock();
1590 }
1591 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001592 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001593}
1594
1595/*
1596 * Called from CHECKSTRSPACE.
1597 */
1598static char *
1599makestrspace(size_t newlen, char *p)
1600{
Denis Vlasenko01631112007-12-16 17:20:38 +00001601 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001602 size_t size = stackblocksize();
1603
1604 for (;;) {
1605 size_t nleft;
1606
1607 size = stackblocksize();
1608 nleft = size - len;
1609 if (nleft >= newlen)
1610 break;
1611 growstackblock();
1612 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001613 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001614}
1615
1616static char *
1617stack_nputstr(const char *s, size_t n, char *p)
1618{
1619 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001620 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001621 return p;
1622}
1623
1624static char *
1625stack_putstr(const char *s, char *p)
1626{
1627 return stack_nputstr(s, strlen(s), p);
1628}
1629
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001630static char *
1631_STPUTC(int c, char *p)
1632{
1633 if (p == sstrend)
1634 p = growstackstr();
1635 *p++ = c;
1636 return p;
1637}
1638
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001639#define STARTSTACKSTR(p) ((p) = stackblock())
1640#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001641#define CHECKSTRSPACE(n, p) do { \
1642 char *q = (p); \
1643 size_t l = (n); \
1644 size_t m = sstrend - q; \
1645 if (l > m) \
1646 (p) = makestrspace(l, q); \
1647} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001648#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001649#define STACKSTRNUL(p) do { \
1650 if ((p) == sstrend) \
1651 (p) = growstackstr(); \
1652 *(p) = '\0'; \
1653} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001654#define STUNPUTC(p) (--(p))
1655#define STTOPC(p) ((p)[-1])
1656#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001657
1658#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001659#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660#define stackstrend() ((void *)sstrend)
1661
1662
1663/* ============ String helpers */
1664
1665/*
1666 * prefix -- see if pfx is a prefix of string.
1667 */
1668static char *
1669prefix(const char *string, const char *pfx)
1670{
1671 while (*pfx) {
1672 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001673 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001674 }
1675 return (char *) string;
1676}
1677
1678/*
1679 * Check for a valid number. This should be elsewhere.
1680 */
1681static int
1682is_number(const char *p)
1683{
1684 do {
1685 if (!isdigit(*p))
1686 return 0;
1687 } while (*++p != '\0');
1688 return 1;
1689}
1690
1691/*
1692 * Convert a string of digits to an integer, printing an error message on
1693 * failure.
1694 */
1695static int
1696number(const char *s)
1697{
1698 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001699 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001700 return atoi(s);
1701}
1702
1703/*
1704 * Produce a possibly single quoted string suitable as input to the shell.
1705 * The return string is allocated on the stack.
1706 */
1707static char *
1708single_quote(const char *s)
1709{
1710 char *p;
1711
1712 STARTSTACKSTR(p);
1713
1714 do {
1715 char *q;
1716 size_t len;
1717
1718 len = strchrnul(s, '\'') - s;
1719
1720 q = p = makestrspace(len + 3, p);
1721
1722 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001723 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001724 *q++ = '\'';
1725 s += len;
1726
1727 STADJUST(q - p, p);
1728
Denys Vlasenkocd716832009-11-28 22:14:02 +01001729 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001730 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001731 len = 0;
1732 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733
1734 q = p = makestrspace(len + 3, p);
1735
1736 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001737 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739
1740 STADJUST(q - p, p);
1741 } while (*s);
1742
Denys Vlasenkocd716832009-11-28 22:14:02 +01001743 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001744
1745 return stackblock();
1746}
1747
1748
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001749/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001750
1751static char **argptr; /* argument list for builtin commands */
1752static char *optionarg; /* set by nextopt (like getopt) */
1753static char *optptr; /* used by nextopt */
1754
1755/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001756 * XXX - should get rid of. Have all builtins use getopt(3).
1757 * The library getopt must have the BSD extension static variable
1758 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001760 * Standard option processing (a la getopt) for builtin routines.
1761 * The only argument that is passed to nextopt is the option string;
1762 * the other arguments are unnecessary. It returns the character,
1763 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001764 */
1765static int
1766nextopt(const char *optstring)
1767{
1768 char *p;
1769 const char *q;
1770 char c;
1771
1772 p = optptr;
1773 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001774 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001775 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001776 if (p == NULL)
1777 return '\0';
1778 if (*p != '-')
1779 return '\0';
1780 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781 return '\0';
1782 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001783 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001784 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001785 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001787 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001788 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001789 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001790 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001791 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001792 if (*++q == ':')
1793 q++;
1794 }
1795 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001796 if (*p == '\0') {
1797 p = *argptr++;
1798 if (p == NULL)
1799 ash_msg_and_raise_error("no arg for -%c option", c);
1800 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001801 optionarg = p;
1802 p = NULL;
1803 }
1804 optptr = p;
1805 return c;
1806}
1807
1808
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001809/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001810
Denis Vlasenko01631112007-12-16 17:20:38 +00001811/*
1812 * The parsefile structure pointed to by the global variable parsefile
1813 * contains information about the current file being read.
1814 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001815struct shparam {
1816 int nparam; /* # of positional parameters (without $0) */
1817#if ENABLE_ASH_GETOPTS
1818 int optind; /* next parameter to be processed by getopts */
1819 int optoff; /* used by getopts */
1820#endif
1821 unsigned char malloced; /* if parameter list dynamically allocated */
1822 char **p; /* parameter list */
1823};
1824
1825/*
1826 * Free the list of positional parameters.
1827 */
1828static void
1829freeparam(volatile struct shparam *param)
1830{
Denis Vlasenko01631112007-12-16 17:20:38 +00001831 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001832 char **ap, **ap1;
1833 ap = ap1 = param->p;
1834 while (*ap)
1835 free(*ap++);
1836 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001837 }
1838}
1839
1840#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001841static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001842#endif
1843
1844struct var {
1845 struct var *next; /* next entry in hash list */
1846 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001847 const char *var_text; /* name=value */
1848 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001849 /* the variable gets set/unset */
1850};
1851
1852struct localvar {
1853 struct localvar *next; /* next local variable in list */
1854 struct var *vp; /* the variable that was made local */
1855 int flags; /* saved flags */
1856 const char *text; /* saved text */
1857};
1858
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001859/* flags */
1860#define VEXPORT 0x01 /* variable is exported */
1861#define VREADONLY 0x02 /* variable cannot be modified */
1862#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1863#define VTEXTFIXED 0x08 /* text is statically allocated */
1864#define VSTACK 0x10 /* text is allocated on the stack */
1865#define VUNSET 0x20 /* the variable is not set */
1866#define VNOFUNC 0x40 /* don't call the callback function */
1867#define VNOSET 0x80 /* do not set variable - just readonly test */
1868#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001869#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001870# define VDYNAMIC 0x200 /* dynamic variable */
1871#else
1872# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001873#endif
1874
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001875
Denis Vlasenko01631112007-12-16 17:20:38 +00001876/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001877#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001878static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001879change_lc_all(const char *value)
1880{
1881 if (value && *value != '\0')
1882 setlocale(LC_ALL, value);
1883}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001884static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001885change_lc_ctype(const char *value)
1886{
1887 if (value && *value != '\0')
1888 setlocale(LC_CTYPE, value);
1889}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001890#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#if ENABLE_ASH_MAIL
1892static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001893static void changemail(const char *var_value) FAST_FUNC;
1894#else
1895# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001896#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001897static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001898#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001899static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001900#endif
1901
Denis Vlasenko01631112007-12-16 17:20:38 +00001902static const struct {
1903 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001904 const char *var_text;
1905 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001906} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001907 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001909 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1910 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001912 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1913 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1914 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1915 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001916#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001917 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918#endif
1919#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001920 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921#endif
1922#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001923 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1924 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001925#endif
1926#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001927 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001928#endif
1929};
1930
Denis Vlasenko0b769642008-07-24 07:54:57 +00001931struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001932
1933struct globals_var {
1934 struct shparam shellparam; /* $@ current positional parameters */
1935 struct redirtab *redirlist;
1936 int g_nullredirs;
1937 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1938 struct var *vartab[VTABSIZE];
1939 struct var varinit[ARRAY_SIZE(varinit_data)];
1940};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001941extern struct globals_var *const ash_ptr_to_globals_var;
1942#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001943#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001944//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001945#define g_nullredirs (G_var.g_nullredirs )
1946#define preverrout_fd (G_var.preverrout_fd)
1947#define vartab (G_var.vartab )
1948#define varinit (G_var.varinit )
1949#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001950 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001951 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1952 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001953 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001954 varinit[i].flags = varinit_data[i].flags; \
1955 varinit[i].var_text = varinit_data[i].var_text; \
1956 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001957 } \
1958} while (0)
1959
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001960#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001961#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001962# define vmail (&vifs)[1]
1963# define vmpath (&vmail)[1]
1964# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001965#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001966# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001967#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001968#define vps1 (&vpath)[1]
1969#define vps2 (&vps1)[1]
1970#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001972# define voptind (&vps4)[1]
1973# if ENABLE_ASH_RANDOM_SUPPORT
1974# define vrandom (&voptind)[1]
1975# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001977# if ENABLE_ASH_RANDOM_SUPPORT
1978# define vrandom (&vps4)[1]
1979# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001981
1982/*
1983 * The following macros access the values of the above variables.
1984 * They have to skip over the name. They return the null string
1985 * for unset variables.
1986 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001987#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001988#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001989#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001990# define mailval() (vmail.var_text + 5)
1991# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001992# define mpathset() ((vmpath.flags & VUNSET) == 0)
1993#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001994#define pathval() (vpath.var_text + 5)
1995#define ps1val() (vps1.var_text + 4)
1996#define ps2val() (vps2.var_text + 4)
1997#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001998#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001999# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002000#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002001
Denis Vlasenko01631112007-12-16 17:20:38 +00002002#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002003static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002004getoptsreset(const char *value)
2005{
2006 shellparam.optind = number(value);
2007 shellparam.optoff = -1;
2008}
2009#endif
2010
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002011/* math.h has these, otherwise define our private copies */
2012#if !ENABLE_SH_MATH_SUPPORT
2013#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2014#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002016 * Return the pointer to the first char which is not part of a legal variable name
2017 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002018 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002019static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002020endofname(const char *name)
2021{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002022 if (!is_name(*name))
2023 return name;
2024 while (*++name) {
2025 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002026 break;
2027 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002028 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002029}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002030#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002031
2032/*
2033 * Compares two strings up to the first = or '\0'. The first
2034 * string must be terminated by '='; the second may be terminated by
2035 * either '=' or '\0'.
2036 */
2037static int
2038varcmp(const char *p, const char *q)
2039{
2040 int c, d;
2041
2042 while ((c = *p) == (d = *q)) {
2043 if (!c || c == '=')
2044 goto out;
2045 p++;
2046 q++;
2047 }
2048 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002049 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002050 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002051 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002052 out:
2053 return c - d;
2054}
2055
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002056/*
2057 * Find the appropriate entry in the hash table from the name.
2058 */
2059static struct var **
2060hashvar(const char *p)
2061{
2062 unsigned hashval;
2063
2064 hashval = ((unsigned char) *p) << 4;
2065 while (*p && *p != '=')
2066 hashval += (unsigned char) *p++;
2067 return &vartab[hashval % VTABSIZE];
2068}
2069
2070static int
2071vpcmp(const void *a, const void *b)
2072{
2073 return varcmp(*(const char **)a, *(const char **)b);
2074}
2075
2076/*
2077 * This routine initializes the builtin variables.
2078 */
2079static void
2080initvar(void)
2081{
2082 struct var *vp;
2083 struct var *end;
2084 struct var **vpp;
2085
2086 /*
2087 * PS1 depends on uid
2088 */
2089#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002090 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002091#else
2092 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002093 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094#endif
2095 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002096 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002097 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002098 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002099 vp->next = *vpp;
2100 *vpp = vp;
2101 } while (++vp < end);
2102}
2103
2104static struct var **
2105findvar(struct var **vpp, const char *name)
2106{
2107 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002108 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002109 break;
2110 }
2111 }
2112 return vpp;
2113}
2114
2115/*
2116 * Find the value of a variable. Returns NULL if not set.
2117 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002118static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119lookupvar(const char *name)
2120{
2121 struct var *v;
2122
2123 v = *findvar(hashvar(name), name);
2124 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002125#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126 /*
2127 * Dynamic variables are implemented roughly the same way they are
2128 * in bash. Namely, they're "special" so long as they aren't unset.
2129 * As soon as they're unset, they're no longer dynamic, and dynamic
2130 * lookup will no longer happen at that point. -- PFM.
2131 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002132 if (v->flags & VDYNAMIC)
2133 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002134#endif
2135 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002136 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137 }
2138 return NULL;
2139}
2140
2141/*
2142 * Search the environment of a builtin command.
2143 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002144static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145bltinlookup(const char *name)
2146{
2147 struct strlist *sp;
2148
2149 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002150 if (varcmp(sp->text, name) == 0)
2151 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002152 }
2153 return lookupvar(name);
2154}
2155
2156/*
2157 * Same as setvar except that the variable and value are passed in
2158 * the first argument as name=value. Since the first argument will
2159 * be actually stored in the table, it should not be a string that
2160 * will go away.
2161 * Called with interrupts off.
2162 */
2163static void
2164setvareq(char *s, int flags)
2165{
2166 struct var *vp, **vpp;
2167
2168 vpp = hashvar(s);
2169 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2170 vp = *findvar(vpp, s);
2171 if (vp) {
2172 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2173 const char *n;
2174
2175 if (flags & VNOSAVE)
2176 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002178 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2179 }
2180
2181 if (flags & VNOSET)
2182 return;
2183
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002184 if (vp->var_func && !(flags & VNOFUNC))
2185 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002187 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2188 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002189
2190 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2191 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002192 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193 if (flags & VNOSET)
2194 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002195 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002196 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002197 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002198 *vpp = vp;
2199 }
2200 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2201 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002202 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002203 vp->flags = flags;
2204}
2205
2206/*
2207 * Set the value of a variable. The flags argument is ored with the
2208 * flags of the variable. If val is NULL, the variable is unset.
2209 */
2210static void
2211setvar(const char *name, const char *val, int flags)
2212{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002213 const char *q;
2214 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002215 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002216 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 size_t vallen;
2218
2219 q = endofname(name);
2220 p = strchrnul(q, '=');
2221 namelen = p - name;
2222 if (!namelen || p != q)
2223 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2224 vallen = 0;
2225 if (val == NULL) {
2226 flags |= VUNSET;
2227 } else {
2228 vallen = strlen(val);
2229 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002230
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231 INT_OFF;
2232 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002233 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002234 if (val) {
2235 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002236 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002237 }
2238 *p = '\0';
2239 setvareq(nameeq, flags | VNOSAVE);
2240 INT_ON;
2241}
2242
Denys Vlasenko03dad222010-01-12 23:29:57 +01002243static void FAST_FUNC
2244setvar2(const char *name, const char *val)
2245{
2246 setvar(name, val, 0);
2247}
2248
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002249#if ENABLE_ASH_GETOPTS
2250/*
2251 * Safe version of setvar, returns 1 on success 0 on failure.
2252 */
2253static int
2254setvarsafe(const char *name, const char *val, int flags)
2255{
2256 int err;
2257 volatile int saveint;
2258 struct jmploc *volatile savehandler = exception_handler;
2259 struct jmploc jmploc;
2260
2261 SAVE_INT(saveint);
2262 if (setjmp(jmploc.loc))
2263 err = 1;
2264 else {
2265 exception_handler = &jmploc;
2266 setvar(name, val, flags);
2267 err = 0;
2268 }
2269 exception_handler = savehandler;
2270 RESTORE_INT(saveint);
2271 return err;
2272}
2273#endif
2274
2275/*
2276 * Unset the specified variable.
2277 */
2278static int
2279unsetvar(const char *s)
2280{
2281 struct var **vpp;
2282 struct var *vp;
2283 int retval;
2284
2285 vpp = findvar(hashvar(s), s);
2286 vp = *vpp;
2287 retval = 2;
2288 if (vp) {
2289 int flags = vp->flags;
2290
2291 retval = 1;
2292 if (flags & VREADONLY)
2293 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002294#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002295 vp->flags &= ~VDYNAMIC;
2296#endif
2297 if (flags & VUNSET)
2298 goto ok;
2299 if ((flags & VSTRFIXED) == 0) {
2300 INT_OFF;
2301 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002302 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002303 *vpp = vp->next;
2304 free(vp);
2305 INT_ON;
2306 } else {
2307 setvar(s, 0, 0);
2308 vp->flags &= ~VEXPORT;
2309 }
2310 ok:
2311 retval = 0;
2312 }
2313 out:
2314 return retval;
2315}
2316
2317/*
2318 * Process a linked list of variable assignments.
2319 */
2320static void
2321listsetvar(struct strlist *list_set_var, int flags)
2322{
2323 struct strlist *lp = list_set_var;
2324
2325 if (!lp)
2326 return;
2327 INT_OFF;
2328 do {
2329 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002330 lp = lp->next;
2331 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002332 INT_ON;
2333}
2334
2335/*
2336 * Generate a list of variables satisfying the given conditions.
2337 */
2338static char **
2339listvars(int on, int off, char ***end)
2340{
2341 struct var **vpp;
2342 struct var *vp;
2343 char **ep;
2344 int mask;
2345
2346 STARTSTACKSTR(ep);
2347 vpp = vartab;
2348 mask = on | off;
2349 do {
2350 for (vp = *vpp; vp; vp = vp->next) {
2351 if ((vp->flags & mask) == on) {
2352 if (ep == stackstrend())
2353 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002354 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002355 }
2356 }
2357 } while (++vpp < vartab + VTABSIZE);
2358 if (ep == stackstrend())
2359 ep = growstackstr();
2360 if (end)
2361 *end = ep;
2362 *ep++ = NULL;
2363 return grabstackstr(ep);
2364}
2365
2366
2367/* ============ Path search helper
2368 *
2369 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002370 * of the path before the first call; path_advance will update
2371 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002372 * the possible path expansions in sequence. If an option (indicated by
2373 * a percent sign) appears in the path entry then the global variable
2374 * pathopt will be set to point to it; otherwise pathopt will be set to
2375 * NULL.
2376 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002377static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378
2379static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002380path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002381{
2382 const char *p;
2383 char *q;
2384 const char *start;
2385 size_t len;
2386
2387 if (*path == NULL)
2388 return NULL;
2389 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002390 for (p = start; *p && *p != ':' && *p != '%'; p++)
2391 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002392 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2393 while (stackblocksize() < len)
2394 growstackblock();
2395 q = stackblock();
2396 if (p != start) {
2397 memcpy(q, start, p - start);
2398 q += p - start;
2399 *q++ = '/';
2400 }
2401 strcpy(q, name);
2402 pathopt = NULL;
2403 if (*p == '%') {
2404 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002405 while (*p && *p != ':')
2406 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407 }
2408 if (*p == ':')
2409 *path = p + 1;
2410 else
2411 *path = NULL;
2412 return stalloc(len);
2413}
2414
2415
2416/* ============ Prompt */
2417
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002418static smallint doprompt; /* if set, prompt the user */
2419static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002420
2421#if ENABLE_FEATURE_EDITING
2422static line_input_t *line_input_state;
2423static const char *cmdedit_prompt;
2424static void
2425putprompt(const char *s)
2426{
2427 if (ENABLE_ASH_EXPAND_PRMT) {
2428 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002429 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002430 return;
2431 }
2432 cmdedit_prompt = s;
2433}
2434#else
2435static void
2436putprompt(const char *s)
2437{
2438 out2str(s);
2439}
2440#endif
2441
2442#if ENABLE_ASH_EXPAND_PRMT
2443/* expandstr() needs parsing machinery, so it is far away ahead... */
2444static const char *expandstr(const char *ps);
2445#else
2446#define expandstr(s) s
2447#endif
2448
2449static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002450setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002451{
2452 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002453 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2454
2455 if (!do_set)
2456 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002457
2458 needprompt = 0;
2459
2460 switch (whichprompt) {
2461 case 1:
2462 prompt = ps1val();
2463 break;
2464 case 2:
2465 prompt = ps2val();
2466 break;
2467 default: /* 0 */
2468 prompt = nullstr;
2469 }
2470#if ENABLE_ASH_EXPAND_PRMT
2471 setstackmark(&smark);
2472 stalloc(stackblocksize());
2473#endif
2474 putprompt(expandstr(prompt));
2475#if ENABLE_ASH_EXPAND_PRMT
2476 popstackmark(&smark);
2477#endif
2478}
2479
2480
2481/* ============ The cd and pwd commands */
2482
2483#define CD_PHYSICAL 1
2484#define CD_PRINT 2
2485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002486static int
2487cdopt(void)
2488{
2489 int flags = 0;
2490 int i, j;
2491
2492 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002493 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494 if (i != j) {
2495 flags ^= CD_PHYSICAL;
2496 j = i;
2497 }
2498 }
2499
2500 return flags;
2501}
2502
2503/*
2504 * Update curdir (the name of the current directory) in response to a
2505 * cd command.
2506 */
2507static const char *
2508updatepwd(const char *dir)
2509{
2510 char *new;
2511 char *p;
2512 char *cdcomppath;
2513 const char *lim;
2514
2515 cdcomppath = ststrdup(dir);
2516 STARTSTACKSTR(new);
2517 if (*dir != '/') {
2518 if (curdir == nullstr)
2519 return 0;
2520 new = stack_putstr(curdir, new);
2521 }
2522 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002523 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002524 if (*dir != '/') {
2525 if (new[-1] != '/')
2526 USTPUTC('/', new);
2527 if (new > lim && *lim == '/')
2528 lim++;
2529 } else {
2530 USTPUTC('/', new);
2531 cdcomppath++;
2532 if (dir[1] == '/' && dir[2] != '/') {
2533 USTPUTC('/', new);
2534 cdcomppath++;
2535 lim++;
2536 }
2537 }
2538 p = strtok(cdcomppath, "/");
2539 while (p) {
2540 switch (*p) {
2541 case '.':
2542 if (p[1] == '.' && p[2] == '\0') {
2543 while (new > lim) {
2544 STUNPUTC(new);
2545 if (new[-1] == '/')
2546 break;
2547 }
2548 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002549 }
2550 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002551 break;
2552 /* fall through */
2553 default:
2554 new = stack_putstr(p, new);
2555 USTPUTC('/', new);
2556 }
2557 p = strtok(0, "/");
2558 }
2559 if (new > lim)
2560 STUNPUTC(new);
2561 *new = 0;
2562 return stackblock();
2563}
2564
2565/*
2566 * Find out what the current directory is. If we already know the current
2567 * directory, this routine returns immediately.
2568 */
2569static char *
2570getpwd(void)
2571{
Denis Vlasenko01631112007-12-16 17:20:38 +00002572 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002573 return dir ? dir : nullstr;
2574}
2575
2576static void
2577setpwd(const char *val, int setold)
2578{
2579 char *oldcur, *dir;
2580
2581 oldcur = dir = curdir;
2582
2583 if (setold) {
2584 setvar("OLDPWD", oldcur, VEXPORT);
2585 }
2586 INT_OFF;
2587 if (physdir != nullstr) {
2588 if (physdir != oldcur)
2589 free(physdir);
2590 physdir = nullstr;
2591 }
2592 if (oldcur == val || !val) {
2593 char *s = getpwd();
2594 physdir = s;
2595 if (!val)
2596 dir = s;
2597 } else
2598 dir = ckstrdup(val);
2599 if (oldcur != dir && oldcur != nullstr) {
2600 free(oldcur);
2601 }
2602 curdir = dir;
2603 INT_ON;
2604 setvar("PWD", dir, VEXPORT);
2605}
2606
2607static void hashcd(void);
2608
2609/*
2610 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2611 * know that the current directory has changed.
2612 */
2613static int
2614docd(const char *dest, int flags)
2615{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002616 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002617 int err;
2618
2619 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2620
2621 INT_OFF;
2622 if (!(flags & CD_PHYSICAL)) {
2623 dir = updatepwd(dest);
2624 if (dir)
2625 dest = dir;
2626 }
2627 err = chdir(dest);
2628 if (err)
2629 goto out;
2630 setpwd(dir, 1);
2631 hashcd();
2632 out:
2633 INT_ON;
2634 return err;
2635}
2636
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002637static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002638cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002639{
2640 const char *dest;
2641 const char *path;
2642 const char *p;
2643 char c;
2644 struct stat statb;
2645 int flags;
2646
2647 flags = cdopt();
2648 dest = *argptr;
2649 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002650 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002651 else if (LONE_DASH(dest)) {
2652 dest = bltinlookup("OLDPWD");
2653 flags |= CD_PRINT;
2654 }
2655 if (!dest)
2656 dest = nullstr;
2657 if (*dest == '/')
2658 goto step7;
2659 if (*dest == '.') {
2660 c = dest[1];
2661 dotdot:
2662 switch (c) {
2663 case '\0':
2664 case '/':
2665 goto step6;
2666 case '.':
2667 c = dest[2];
2668 if (c != '.')
2669 goto dotdot;
2670 }
2671 }
2672 if (!*dest)
2673 dest = ".";
2674 path = bltinlookup("CDPATH");
2675 if (!path) {
2676 step6:
2677 step7:
2678 p = dest;
2679 goto docd;
2680 }
2681 do {
2682 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002683 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002684 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2685 if (c && c != ':')
2686 flags |= CD_PRINT;
2687 docd:
2688 if (!docd(p, flags))
2689 goto out;
2690 break;
2691 }
2692 } while (path);
2693 ash_msg_and_raise_error("can't cd to %s", dest);
2694 /* NOTREACHED */
2695 out:
2696 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002697 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002698 return 0;
2699}
2700
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002701static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002702pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002703{
2704 int flags;
2705 const char *dir = curdir;
2706
2707 flags = cdopt();
2708 if (flags) {
2709 if (physdir == nullstr)
2710 setpwd(dir, 0);
2711 dir = physdir;
2712 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002713 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002714 return 0;
2715}
2716
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002717
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002718/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002719
Denis Vlasenko834dee72008-10-07 09:18:30 +00002720
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002721#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002722
Eric Andersenc470f442003-07-28 09:56:35 +00002723/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002724#define CWORD 0 /* character is nothing special */
2725#define CNL 1 /* newline character */
2726#define CBACK 2 /* a backslash character */
2727#define CSQUOTE 3 /* single quote */
2728#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002729#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002730#define CBQUOTE 6 /* backwards single quote */
2731#define CVAR 7 /* a dollar sign */
2732#define CENDVAR 8 /* a '}' character */
2733#define CLP 9 /* a left paren in arithmetic */
2734#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002735#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002736#define CCTL 12 /* like CWORD, except it must be escaped */
2737#define CSPCL 13 /* these terminate a word */
2738#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002739
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002740#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002741#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002742# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002743#endif
2744
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002745#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002746
Mike Frysinger98c52642009-04-02 10:02:37 +00002747#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002748# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002749#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002751#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002752static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002753#if ENABLE_ASH_ALIAS
2754 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2755#endif
2756 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2757 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2758 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2759 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2760 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2761 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2762 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2763 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2764 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2765 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2766 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002767#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002768 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2769 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2770 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2771#endif
2772#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002773};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002774/* Constants below must match table above */
2775enum {
2776#if ENABLE_ASH_ALIAS
2777 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2778#endif
2779 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2780 CNL_CNL_CNL_CNL , /* 2 */
2781 CWORD_CCTL_CCTL_CWORD , /* 3 */
2782 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2783 CVAR_CVAR_CWORD_CVAR , /* 5 */
2784 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2785 CSPCL_CWORD_CWORD_CLP , /* 7 */
2786 CSPCL_CWORD_CWORD_CRP , /* 8 */
2787 CBACK_CBACK_CCTL_CBACK , /* 9 */
2788 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2789 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2790 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2791 CWORD_CWORD_CWORD_CWORD , /* 13 */
2792 CCTL_CCTL_CCTL_CCTL , /* 14 */
2793};
Eric Andersen2870d962001-07-02 17:27:21 +00002794
Denys Vlasenkocd716832009-11-28 22:14:02 +01002795/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2796 * caller must ensure proper cast on it if c is *char_ptr!
2797 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002798/* Values for syntax param */
2799#define BASESYNTAX 0 /* not in quotes */
2800#define DQSYNTAX 1 /* in double quotes */
2801#define SQSYNTAX 2 /* in single quotes */
2802#define ARISYNTAX 3 /* in arithmetic */
2803#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002804
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002805#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002806
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002807static int
2808SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002809{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002810 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002811# if ENABLE_ASH_ALIAS
2812 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002813 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2814 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2815 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2816 11, 3 /* "}~" */
2817 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002818# else
2819 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002820 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2821 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2822 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2823 10, 2 /* "}~" */
2824 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002825# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002826 const char *s;
2827 int indx;
2828
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002829 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002830 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002831# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002832 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002833 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002834 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002835# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002836 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002837 /* Cast is purely for paranoia here,
2838 * just in case someone passed signed char to us */
2839 if ((unsigned char)c >= CTL_FIRST
2840 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002841 ) {
2842 return CCTL;
2843 }
2844 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002845 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002846 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002847 indx = syntax_index_table[s - spec_symbls];
2848 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002849 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002850}
2851
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002852#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002853
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002854static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002855 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002856 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2866 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2867 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2889 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2891 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2893 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2895 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2896 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2897 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2898 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2899 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2901 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2903 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2904 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2916 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2917 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2918 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2919 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2920 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2948 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2949 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2950 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2953 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2981 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2982 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2983 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2984 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2986 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2987 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2988 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2989 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2990 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2991 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2992 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2993 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3107 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3108 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3109 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3110 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3111 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003112 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003113# if ENABLE_ASH_ALIAS
3114 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3115# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003116};
3117
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003118# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003119
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003120#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003121
Eric Andersen2870d962001-07-02 17:27:21 +00003122
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003123/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003124
Denis Vlasenko131ae172007-02-18 13:00:19 +00003125#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003126
3127#define ALIASINUSE 1
3128#define ALIASDEAD 2
3129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003130struct alias {
3131 struct alias *next;
3132 char *name;
3133 char *val;
3134 int flag;
3135};
3136
Denis Vlasenko01631112007-12-16 17:20:38 +00003137
3138static struct alias **atab; // [ATABSIZE];
3139#define INIT_G_alias() do { \
3140 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3141} while (0)
3142
Eric Andersen2870d962001-07-02 17:27:21 +00003143
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003144static struct alias **
3145__lookupalias(const char *name) {
3146 unsigned int hashval;
3147 struct alias **app;
3148 const char *p;
3149 unsigned int ch;
3150
3151 p = name;
3152
3153 ch = (unsigned char)*p;
3154 hashval = ch << 4;
3155 while (ch) {
3156 hashval += ch;
3157 ch = (unsigned char)*++p;
3158 }
3159 app = &atab[hashval % ATABSIZE];
3160
3161 for (; *app; app = &(*app)->next) {
3162 if (strcmp(name, (*app)->name) == 0) {
3163 break;
3164 }
3165 }
3166
3167 return app;
3168}
3169
3170static struct alias *
3171lookupalias(const char *name, int check)
3172{
3173 struct alias *ap = *__lookupalias(name);
3174
3175 if (check && ap && (ap->flag & ALIASINUSE))
3176 return NULL;
3177 return ap;
3178}
3179
3180static struct alias *
3181freealias(struct alias *ap)
3182{
3183 struct alias *next;
3184
3185 if (ap->flag & ALIASINUSE) {
3186 ap->flag |= ALIASDEAD;
3187 return ap;
3188 }
3189
3190 next = ap->next;
3191 free(ap->name);
3192 free(ap->val);
3193 free(ap);
3194 return next;
3195}
Eric Andersencb57d552001-06-28 07:25:16 +00003196
Eric Andersenc470f442003-07-28 09:56:35 +00003197static void
3198setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003199{
3200 struct alias *ap, **app;
3201
3202 app = __lookupalias(name);
3203 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003204 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003205 if (ap) {
3206 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003207 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003208 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003209 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003210 ap->flag &= ~ALIASDEAD;
3211 } else {
3212 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003213 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003214 ap->name = ckstrdup(name);
3215 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003216 /*ap->flag = 0; - ckzalloc did it */
3217 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003218 *app = ap;
3219 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003220 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003221}
3222
Eric Andersenc470f442003-07-28 09:56:35 +00003223static int
3224unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003225{
Eric Andersencb57d552001-06-28 07:25:16 +00003226 struct alias **app;
3227
3228 app = __lookupalias(name);
3229
3230 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003231 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003232 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003233 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003234 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003235 }
3236
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003237 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003238}
3239
Eric Andersenc470f442003-07-28 09:56:35 +00003240static void
3241rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003242{
Eric Andersencb57d552001-06-28 07:25:16 +00003243 struct alias *ap, **app;
3244 int i;
3245
Denis Vlasenkob012b102007-02-19 22:43:01 +00003246 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003247 for (i = 0; i < ATABSIZE; i++) {
3248 app = &atab[i];
3249 for (ap = *app; ap; ap = *app) {
3250 *app = freealias(*app);
3251 if (ap == *app) {
3252 app = &ap->next;
3253 }
3254 }
3255 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003256 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003257}
3258
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003259static void
3260printalias(const struct alias *ap)
3261{
3262 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3263}
3264
Eric Andersencb57d552001-06-28 07:25:16 +00003265/*
3266 * TODO - sort output
3267 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003268static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003269aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003270{
3271 char *n, *v;
3272 int ret = 0;
3273 struct alias *ap;
3274
Denis Vlasenko68404f12008-03-17 09:00:54 +00003275 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003276 int i;
3277
Denis Vlasenko68404f12008-03-17 09:00:54 +00003278 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003279 for (ap = atab[i]; ap; ap = ap->next) {
3280 printalias(ap);
3281 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003282 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003283 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003284 }
3285 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003286 v = strchr(n+1, '=');
3287 if (v == NULL) { /* n+1: funny ksh stuff */
3288 ap = *__lookupalias(n);
3289 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003290 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003291 ret = 1;
3292 } else
3293 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003294 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003295 *v++ = '\0';
3296 setalias(n, v);
3297 }
3298 }
3299
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003300 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003301}
3302
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003303static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003304unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003305{
3306 int i;
3307
3308 while ((i = nextopt("a")) != '\0') {
3309 if (i == 'a') {
3310 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003311 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003312 }
3313 }
3314 for (i = 0; *argptr; argptr++) {
3315 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003316 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003317 i = 1;
3318 }
3319 }
3320
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003321 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003322}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003323
Denis Vlasenko131ae172007-02-18 13:00:19 +00003324#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003325
Eric Andersenc470f442003-07-28 09:56:35 +00003326
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327/* ============ jobs.c */
3328
3329/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003330#define FORK_FG 0
3331#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332#define FORK_NOJOB 2
3333
3334/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003335#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3336#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3337#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339/*
3340 * A job structure contains information about a job. A job is either a
3341 * single process or a set of processes contained in a pipeline. In the
3342 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3343 * array of pids.
3344 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003345struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003346 pid_t ps_pid; /* process id */
3347 int ps_status; /* last process status from wait() */
3348 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003349};
3350
3351struct job {
3352 struct procstat ps0; /* status of process */
3353 struct procstat *ps; /* status or processes when more than one */
3354#if JOBS
3355 int stopstatus; /* status of a stopped job */
3356#endif
3357 uint32_t
3358 nprocs: 16, /* number of processes */
3359 state: 8,
3360#define JOBRUNNING 0 /* at least one proc running */
3361#define JOBSTOPPED 1 /* all procs are stopped */
3362#define JOBDONE 2 /* all procs are completed */
3363#if JOBS
3364 sigint: 1, /* job was killed by SIGINT */
3365 jobctl: 1, /* job running under job control */
3366#endif
3367 waited: 1, /* true if this entry has been waited for */
3368 used: 1, /* true if this entry is in used */
3369 changed: 1; /* true if status has changed */
3370 struct job *prev_job; /* previous job */
3371};
3372
Denis Vlasenko68404f12008-03-17 09:00:54 +00003373static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003374static int forkshell(struct job *, union node *, int);
3375static int waitforjob(struct job *);
3376
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003377#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003378enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003379#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003381static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003383#endif
3384
3385/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003386 * Ignore a signal.
3387 */
3388static void
3389ignoresig(int signo)
3390{
3391 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3392 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3393 /* No, need to do it */
3394 signal(signo, SIG_IGN);
3395 }
3396 sigmode[signo - 1] = S_HARD_IGN;
3397}
3398
3399/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003400 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003401 */
3402static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003403signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003404{
3405 gotsig[signo - 1] = 1;
3406
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003407 if (signo == SIGINT && !trap[SIGINT]) {
3408 if (!suppress_int) {
3409 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003410 raise_interrupt(); /* does not return */
3411 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003412 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003413 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003414 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003415 }
3416}
3417
3418/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003419 * Set the signal handler for the specified signal. The routine figures
3420 * out what it should be set to.
3421 */
3422static void
3423setsignal(int signo)
3424{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003425 char *t;
3426 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003427 struct sigaction act;
3428
3429 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003430 new_act = S_DFL;
3431 if (t != NULL) { /* trap for this sig is set */
3432 new_act = S_CATCH;
3433 if (t[0] == '\0') /* trap is "": ignore this sig */
3434 new_act = S_IGN;
3435 }
3436
3437 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003438 switch (signo) {
3439 case SIGINT:
3440 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003441 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003442 break;
3443 case SIGQUIT:
3444#if DEBUG
3445 if (debug)
3446 break;
3447#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003448 /* man bash:
3449 * "In all cases, bash ignores SIGQUIT. Non-builtin
3450 * commands run by bash have signal handlers
3451 * set to the values inherited by the shell
3452 * from its parent". */
3453 new_act = S_IGN;
3454 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003455 case SIGTERM:
3456 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003457 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003458 break;
3459#if JOBS
3460 case SIGTSTP:
3461 case SIGTTOU:
3462 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003463 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003464 break;
3465#endif
3466 }
3467 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003468//TODO: if !rootshell, we reset SIGQUIT to DFL,
3469//whereas we have to restore it to what shell got on entry
3470//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471
3472 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003473 cur_act = *t;
3474 if (cur_act == 0) {
3475 /* current setting is not yet known */
3476 if (sigaction(signo, NULL, &act)) {
3477 /* pretend it worked; maybe we should give a warning,
3478 * but other shells don't. We don't alter sigmode,
3479 * so we retry every time.
3480 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003481 return;
3482 }
3483 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003484 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003485 if (mflag
3486 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3487 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003488 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003489 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490 }
3491 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003492 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003493 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003494
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003495 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003496 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003497 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003498 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003499 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3500 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003501 break;
3502 case S_IGN:
3503 act.sa_handler = SIG_IGN;
3504 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003505 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003506 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003507
3508 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509}
3510
3511/* mode flags for set_curjob */
3512#define CUR_DELETE 2
3513#define CUR_RUNNING 1
3514#define CUR_STOPPED 0
3515
3516/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003517#define DOWAIT_NONBLOCK WNOHANG
3518#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519
3520#if JOBS
3521/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003522static int initialpgrp; //references:2
3523static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524#endif
3525/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003526static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003527/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003528static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003530static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003532static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003533
3534static void
3535set_curjob(struct job *jp, unsigned mode)
3536{
3537 struct job *jp1;
3538 struct job **jpp, **curp;
3539
3540 /* first remove from list */
3541 jpp = curp = &curjob;
3542 do {
3543 jp1 = *jpp;
3544 if (jp1 == jp)
3545 break;
3546 jpp = &jp1->prev_job;
3547 } while (1);
3548 *jpp = jp1->prev_job;
3549
3550 /* Then re-insert in correct position */
3551 jpp = curp;
3552 switch (mode) {
3553 default:
3554#if DEBUG
3555 abort();
3556#endif
3557 case CUR_DELETE:
3558 /* job being deleted */
3559 break;
3560 case CUR_RUNNING:
3561 /* newly created job or backgrounded job,
3562 put after all stopped jobs. */
3563 do {
3564 jp1 = *jpp;
3565#if JOBS
3566 if (!jp1 || jp1->state != JOBSTOPPED)
3567#endif
3568 break;
3569 jpp = &jp1->prev_job;
3570 } while (1);
3571 /* FALLTHROUGH */
3572#if JOBS
3573 case CUR_STOPPED:
3574#endif
3575 /* newly stopped job - becomes curjob */
3576 jp->prev_job = *jpp;
3577 *jpp = jp;
3578 break;
3579 }
3580}
3581
3582#if JOBS || DEBUG
3583static int
3584jobno(const struct job *jp)
3585{
3586 return jp - jobtab + 1;
3587}
3588#endif
3589
3590/*
3591 * Convert a job name to a job structure.
3592 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003593#if !JOBS
3594#define getjob(name, getctl) getjob(name)
3595#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003596static struct job *
3597getjob(const char *name, int getctl)
3598{
3599 struct job *jp;
3600 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003601 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003602 unsigned num;
3603 int c;
3604 const char *p;
3605 char *(*match)(const char *, const char *);
3606
3607 jp = curjob;
3608 p = name;
3609 if (!p)
3610 goto currentjob;
3611
3612 if (*p != '%')
3613 goto err;
3614
3615 c = *++p;
3616 if (!c)
3617 goto currentjob;
3618
3619 if (!p[1]) {
3620 if (c == '+' || c == '%') {
3621 currentjob:
3622 err_msg = "No current job";
3623 goto check;
3624 }
3625 if (c == '-') {
3626 if (jp)
3627 jp = jp->prev_job;
3628 err_msg = "No previous job";
3629 check:
3630 if (!jp)
3631 goto err;
3632 goto gotit;
3633 }
3634 }
3635
3636 if (is_number(p)) {
3637 num = atoi(p);
3638 if (num < njobs) {
3639 jp = jobtab + num - 1;
3640 if (jp->used)
3641 goto gotit;
3642 goto err;
3643 }
3644 }
3645
3646 match = prefix;
3647 if (*p == '?') {
3648 match = strstr;
3649 p++;
3650 }
3651
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003652 found = NULL;
3653 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003654 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655 if (found)
3656 goto err;
3657 found = jp;
3658 err_msg = "%s: ambiguous";
3659 }
3660 jp = jp->prev_job;
3661 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003662 if (!found)
3663 goto err;
3664 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003665
3666 gotit:
3667#if JOBS
3668 err_msg = "job %s not created under job control";
3669 if (getctl && jp->jobctl == 0)
3670 goto err;
3671#endif
3672 return jp;
3673 err:
3674 ash_msg_and_raise_error(err_msg, name);
3675}
3676
3677/*
3678 * Mark a job structure as unused.
3679 */
3680static void
3681freejob(struct job *jp)
3682{
3683 struct procstat *ps;
3684 int i;
3685
3686 INT_OFF;
3687 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003688 if (ps->ps_cmd != nullstr)
3689 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003690 }
3691 if (jp->ps != &jp->ps0)
3692 free(jp->ps);
3693 jp->used = 0;
3694 set_curjob(jp, CUR_DELETE);
3695 INT_ON;
3696}
3697
3698#if JOBS
3699static void
3700xtcsetpgrp(int fd, pid_t pgrp)
3701{
3702 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003703 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003704}
3705
3706/*
3707 * Turn job control on and off.
3708 *
3709 * Note: This code assumes that the third arg to ioctl is a character
3710 * pointer, which is true on Berkeley systems but not System V. Since
3711 * System V doesn't have job control yet, this isn't a problem now.
3712 *
3713 * Called with interrupts off.
3714 */
3715static void
3716setjobctl(int on)
3717{
3718 int fd;
3719 int pgrp;
3720
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003721 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003722 return;
3723 if (on) {
3724 int ofd;
3725 ofd = fd = open(_PATH_TTY, O_RDWR);
3726 if (fd < 0) {
3727 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3728 * That sometimes helps to acquire controlling tty.
3729 * Obviously, a workaround for bugs when someone
3730 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003731 fd = 2;
3732 while (!isatty(fd))
3733 if (--fd < 0)
3734 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003735 }
3736 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003737 if (ofd >= 0)
3738 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 if (fd < 0)
3740 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003741 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003742 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003743 do { /* while we are in the background */
3744 pgrp = tcgetpgrp(fd);
3745 if (pgrp < 0) {
3746 out:
3747 ash_msg("can't access tty; job control turned off");
3748 mflag = on = 0;
3749 goto close;
3750 }
3751 if (pgrp == getpgrp())
3752 break;
3753 killpg(0, SIGTTIN);
3754 } while (1);
3755 initialpgrp = pgrp;
3756
3757 setsignal(SIGTSTP);
3758 setsignal(SIGTTOU);
3759 setsignal(SIGTTIN);
3760 pgrp = rootpid;
3761 setpgid(0, pgrp);
3762 xtcsetpgrp(fd, pgrp);
3763 } else {
3764 /* turning job control off */
3765 fd = ttyfd;
3766 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003767 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003768 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003769 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 setpgid(0, pgrp);
3771 setsignal(SIGTSTP);
3772 setsignal(SIGTTOU);
3773 setsignal(SIGTTIN);
3774 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003775 if (fd >= 0)
3776 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003777 fd = -1;
3778 }
3779 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003780 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003781}
3782
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003783static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784killcmd(int argc, char **argv)
3785{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003786 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003787 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003788 do {
3789 if (argv[i][0] == '%') {
3790 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003791 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003792 /* Enough space for ' -NNN<nul>' */
3793 argv[i] = alloca(sizeof(int)*3 + 3);
3794 /* kill_main has matching code to expect
3795 * leading space. Needed to not confuse
3796 * negative pids with "kill -SIGNAL_NO" syntax */
3797 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003799 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003801 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802}
3803
3804static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003805showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003806{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003807 struct procstat *ps;
3808 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003809
Denys Vlasenko285ad152009-12-04 23:02:27 +01003810 psend = jp->ps + jp->nprocs;
3811 for (ps = jp->ps + 1; ps < psend; ps++)
3812 printf(" | %s", ps->ps_cmd);
3813 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003814 flush_stdout_stderr();
3815}
3816
3817
3818static int
3819restartjob(struct job *jp, int mode)
3820{
3821 struct procstat *ps;
3822 int i;
3823 int status;
3824 pid_t pgid;
3825
3826 INT_OFF;
3827 if (jp->state == JOBDONE)
3828 goto out;
3829 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003830 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003831 if (mode == FORK_FG)
3832 xtcsetpgrp(ttyfd, pgid);
3833 killpg(pgid, SIGCONT);
3834 ps = jp->ps;
3835 i = jp->nprocs;
3836 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003837 if (WIFSTOPPED(ps->ps_status)) {
3838 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003840 ps++;
3841 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842 out:
3843 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3844 INT_ON;
3845 return status;
3846}
3847
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003848static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003849fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003850{
3851 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003852 int mode;
3853 int retval;
3854
3855 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3856 nextopt(nullstr);
3857 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003858 do {
3859 jp = getjob(*argv, 1);
3860 if (mode == FORK_BG) {
3861 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003862 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003863 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003864 out1str(jp->ps[0].ps_cmd);
3865 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003866 retval = restartjob(jp, mode);
3867 } while (*argv && *++argv);
3868 return retval;
3869}
3870#endif
3871
3872static int
3873sprint_status(char *s, int status, int sigonly)
3874{
3875 int col;
3876 int st;
3877
3878 col = 0;
3879 if (!WIFEXITED(status)) {
3880#if JOBS
3881 if (WIFSTOPPED(status))
3882 st = WSTOPSIG(status);
3883 else
3884#endif
3885 st = WTERMSIG(status);
3886 if (sigonly) {
3887 if (st == SIGINT || st == SIGPIPE)
3888 goto out;
3889#if JOBS
3890 if (WIFSTOPPED(status))
3891 goto out;
3892#endif
3893 }
3894 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003895//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896 col = fmtstr(s, 32, strsignal(st));
3897 if (WCOREDUMP(status)) {
3898 col += fmtstr(s + col, 16, " (core dumped)");
3899 }
3900 } else if (!sigonly) {
3901 st = WEXITSTATUS(status);
3902 if (st)
3903 col = fmtstr(s, 16, "Done(%d)", st);
3904 else
3905 col = fmtstr(s, 16, "Done");
3906 }
3907 out:
3908 return col;
3909}
3910
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003911static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003912dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003913{
3914 int pid;
3915 int status;
3916 struct job *jp;
3917 struct job *thisjob;
3918 int state;
3919
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003920 TRACE(("dowait(0x%x) called\n", wait_flags));
3921
3922 /* Do a wait system call. If job control is compiled in, we accept
3923 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3924 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003925 if (doing_jobctl)
3926 wait_flags |= WUNTRACED;
3927 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003928 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3929 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003930 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003931 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003932
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003933 INT_OFF;
3934 thisjob = NULL;
3935 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003936 struct procstat *ps;
3937 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 if (jp->state == JOBDONE)
3939 continue;
3940 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003941 ps = jp->ps;
3942 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003943 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003944 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003945 TRACE(("Job %d: changing status of proc %d "
3946 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003947 jobno(jp), pid, ps->ps_status, status));
3948 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 thisjob = jp;
3950 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003951 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003952 state = JOBRUNNING;
3953#if JOBS
3954 if (state == JOBRUNNING)
3955 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003956 if (WIFSTOPPED(ps->ps_status)) {
3957 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003958 state = JOBSTOPPED;
3959 }
3960#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003961 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003962 if (thisjob)
3963 goto gotjob;
3964 }
3965#if JOBS
3966 if (!WIFSTOPPED(status))
3967#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 jobless--;
3969 goto out;
3970
3971 gotjob:
3972 if (state != JOBRUNNING) {
3973 thisjob->changed = 1;
3974
3975 if (thisjob->state != state) {
3976 TRACE(("Job %d: changing state from %d to %d\n",
3977 jobno(thisjob), thisjob->state, state));
3978 thisjob->state = state;
3979#if JOBS
3980 if (state == JOBSTOPPED) {
3981 set_curjob(thisjob, CUR_STOPPED);
3982 }
3983#endif
3984 }
3985 }
3986
3987 out:
3988 INT_ON;
3989
3990 if (thisjob && thisjob == job) {
3991 char s[48 + 1];
3992 int len;
3993
3994 len = sprint_status(s, status, 1);
3995 if (len) {
3996 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003997 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003998 out2str(s);
3999 }
4000 }
4001 return pid;
4002}
4003
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004004static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004005blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004006{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004007 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004008 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004009 raise_exception(EXSIG);
4010 return pid;
4011}
4012
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004013#if JOBS
4014static void
4015showjob(FILE *out, struct job *jp, int mode)
4016{
4017 struct procstat *ps;
4018 struct procstat *psend;
4019 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004020 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004021 char s[80];
4022
4023 ps = jp->ps;
4024
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004025 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004027 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004028 return;
4029 }
4030
4031 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004032 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004033
4034 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004035 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004036 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004037 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004038
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004039 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004040 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004041
4042 psend = ps + jp->nprocs;
4043
4044 if (jp->state == JOBRUNNING) {
4045 strcpy(s + col, "Running");
4046 col += sizeof("Running") - 1;
4047 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004048 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004049 if (jp->state == JOBSTOPPED)
4050 status = jp->stopstatus;
4051 col += sprint_status(s + col, status, 0);
4052 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004053 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004055 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4056 * or prints several "PID | <cmdN>" lines,
4057 * depending on SHOW_PIDS bit.
4058 * We do not print status of individual processes
4059 * between PID and <cmdN>. bash does it, but not very well:
4060 * first line shows overall job status, not process status,
4061 * making it impossible to know 1st process status.
4062 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004064 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004066 s[0] = '\0';
4067 col = 33;
4068 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004069 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004071 fprintf(out, "%s%*c%s%s",
4072 s,
4073 33 - col >= 0 ? 33 - col : 0, ' ',
4074 ps == jp->ps ? "" : "| ",
4075 ps->ps_cmd
4076 );
4077 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004078 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079
4080 jp->changed = 0;
4081
4082 if (jp->state == JOBDONE) {
4083 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4084 freejob(jp);
4085 }
4086}
4087
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004088/*
4089 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4090 * statuses have changed since the last call to showjobs.
4091 */
4092static void
4093showjobs(FILE *out, int mode)
4094{
4095 struct job *jp;
4096
Denys Vlasenko883cea42009-07-11 15:31:59 +02004097 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004098
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004099 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004100 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004101 continue;
4102
4103 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004104 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004105 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004106 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107 }
4108}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004109
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004110static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004111jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004112{
4113 int mode, m;
4114
4115 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004116 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004117 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004118 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004119 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004120 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004121 }
4122
4123 argv = argptr;
4124 if (*argv) {
4125 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004126 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004127 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004128 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004129 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004130 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004131
4132 return 0;
4133}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134#endif /* JOBS */
4135
Michael Abbott359da5e2009-12-04 23:03:29 +01004136/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004137static int
4138getstatus(struct job *job)
4139{
4140 int status;
4141 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004142 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004143
Michael Abbott359da5e2009-12-04 23:03:29 +01004144 /* Fetch last member's status */
4145 ps = job->ps + job->nprocs - 1;
4146 status = ps->ps_status;
4147 if (pipefail) {
4148 /* "set -o pipefail" mode: use last _nonzero_ status */
4149 while (status == 0 && --ps >= job->ps)
4150 status = ps->ps_status;
4151 }
4152
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004153 retval = WEXITSTATUS(status);
4154 if (!WIFEXITED(status)) {
4155#if JOBS
4156 retval = WSTOPSIG(status);
4157 if (!WIFSTOPPED(status))
4158#endif
4159 {
4160 /* XXX: limits number of signals */
4161 retval = WTERMSIG(status);
4162#if JOBS
4163 if (retval == SIGINT)
4164 job->sigint = 1;
4165#endif
4166 }
4167 retval += 128;
4168 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004169 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170 jobno(job), job->nprocs, status, retval));
4171 return retval;
4172}
4173
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004174static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004175waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176{
4177 struct job *job;
4178 int retval;
4179 struct job *jp;
4180
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004181 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004182 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004183
4184 nextopt(nullstr);
4185 retval = 0;
4186
4187 argv = argptr;
4188 if (!*argv) {
4189 /* wait for all jobs */
4190 for (;;) {
4191 jp = curjob;
4192 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004193 if (!jp) /* no running procs */
4194 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004195 if (jp->state == JOBRUNNING)
4196 break;
4197 jp->waited = 1;
4198 jp = jp->prev_job;
4199 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004200 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004201 /* man bash:
4202 * "When bash is waiting for an asynchronous command via
4203 * the wait builtin, the reception of a signal for which a trap
4204 * has been set will cause the wait builtin to return immediately
4205 * with an exit status greater than 128, immediately after which
4206 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004207 *
4208 * blocking_wait_with_raise_on_sig raises signal handlers
4209 * if it gets no pid (pid < 0). However,
4210 * if child sends us a signal *and immediately exits*,
4211 * blocking_wait_with_raise_on_sig gets pid > 0
4212 * and does not handle pending_sig. Check this case: */
4213 if (pending_sig)
4214 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004215 }
4216 }
4217
4218 retval = 127;
4219 do {
4220 if (**argv != '%') {
4221 pid_t pid = number(*argv);
4222 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004223 while (1) {
4224 if (!job)
4225 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004226 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004227 break;
4228 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004229 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004230 } else
4231 job = getjob(*argv, 0);
4232 /* loop until process terminated or stopped */
4233 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004234 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004235 job->waited = 1;
4236 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004237 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004238 } while (*++argv);
4239
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004240 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004241 return retval;
4242}
4243
4244static struct job *
4245growjobtab(void)
4246{
4247 size_t len;
4248 ptrdiff_t offset;
4249 struct job *jp, *jq;
4250
4251 len = njobs * sizeof(*jp);
4252 jq = jobtab;
4253 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4254
4255 offset = (char *)jp - (char *)jq;
4256 if (offset) {
4257 /* Relocate pointers */
4258 size_t l = len;
4259
4260 jq = (struct job *)((char *)jq + l);
4261 while (l) {
4262 l -= sizeof(*jp);
4263 jq--;
4264#define joff(p) ((struct job *)((char *)(p) + l))
4265#define jmove(p) (p) = (void *)((char *)(p) + offset)
4266 if (joff(jp)->ps == &jq->ps0)
4267 jmove(joff(jp)->ps);
4268 if (joff(jp)->prev_job)
4269 jmove(joff(jp)->prev_job);
4270 }
4271 if (curjob)
4272 jmove(curjob);
4273#undef joff
4274#undef jmove
4275 }
4276
4277 njobs += 4;
4278 jobtab = jp;
4279 jp = (struct job *)((char *)jp + len);
4280 jq = jp + 3;
4281 do {
4282 jq->used = 0;
4283 } while (--jq >= jp);
4284 return jp;
4285}
4286
4287/*
4288 * Return a new job structure.
4289 * Called with interrupts off.
4290 */
4291static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004292makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004293{
4294 int i;
4295 struct job *jp;
4296
4297 for (i = njobs, jp = jobtab; ; jp++) {
4298 if (--i < 0) {
4299 jp = growjobtab();
4300 break;
4301 }
4302 if (jp->used == 0)
4303 break;
4304 if (jp->state != JOBDONE || !jp->waited)
4305 continue;
4306#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004307 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004308 continue;
4309#endif
4310 freejob(jp);
4311 break;
4312 }
4313 memset(jp, 0, sizeof(*jp));
4314#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004315 /* jp->jobctl is a bitfield.
4316 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004317 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004318 jp->jobctl = 1;
4319#endif
4320 jp->prev_job = curjob;
4321 curjob = jp;
4322 jp->used = 1;
4323 jp->ps = &jp->ps0;
4324 if (nprocs > 1) {
4325 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4326 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004327 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004328 jobno(jp)));
4329 return jp;
4330}
4331
4332#if JOBS
4333/*
4334 * Return a string identifying a command (to be printed by the
4335 * jobs command).
4336 */
4337static char *cmdnextc;
4338
4339static void
4340cmdputs(const char *s)
4341{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004342 static const char vstype[VSTYPE + 1][3] = {
4343 "", "}", "-", "+", "?", "=",
4344 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004345 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004346 };
4347
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004348 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004349 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004350 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004351 unsigned char c;
4352 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004353 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004354
Denys Vlasenko46a14772009-12-10 21:27:13 +01004355 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004356 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4357 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004358 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004359 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004360 switch (c) {
4361 case CTLESC:
4362 c = *p++;
4363 break;
4364 case CTLVAR:
4365 subtype = *p++;
4366 if ((subtype & VSTYPE) == VSLENGTH)
4367 str = "${#";
4368 else
4369 str = "${";
4370 if (!(subtype & VSQUOTE) == !(quoted & 1))
4371 goto dostr;
4372 quoted ^= 1;
4373 c = '"';
4374 break;
4375 case CTLENDVAR:
4376 str = "\"}" + !(quoted & 1);
4377 quoted >>= 1;
4378 subtype = 0;
4379 goto dostr;
4380 case CTLBACKQ:
4381 str = "$(...)";
4382 goto dostr;
4383 case CTLBACKQ+CTLQUOTE:
4384 str = "\"$(...)\"";
4385 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004386#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 case CTLARI:
4388 str = "$((";
4389 goto dostr;
4390 case CTLENDARI:
4391 str = "))";
4392 goto dostr;
4393#endif
4394 case CTLQUOTEMARK:
4395 quoted ^= 1;
4396 c = '"';
4397 break;
4398 case '=':
4399 if (subtype == 0)
4400 break;
4401 if ((subtype & VSTYPE) != VSNORMAL)
4402 quoted <<= 1;
4403 str = vstype[subtype & VSTYPE];
4404 if (subtype & VSNUL)
4405 c = ':';
4406 else
4407 goto checkstr;
4408 break;
4409 case '\'':
4410 case '\\':
4411 case '"':
4412 case '$':
4413 /* These can only happen inside quotes */
4414 cc[0] = c;
4415 str = cc;
4416 c = '\\';
4417 break;
4418 default:
4419 break;
4420 }
4421 USTPUTC(c, nextc);
4422 checkstr:
4423 if (!str)
4424 continue;
4425 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004426 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004427 USTPUTC(c, nextc);
4428 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004429 } /* while *p++ not NUL */
4430
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004431 if (quoted & 1) {
4432 USTPUTC('"', nextc);
4433 }
4434 *nextc = 0;
4435 cmdnextc = nextc;
4436}
4437
4438/* cmdtxt() and cmdlist() call each other */
4439static void cmdtxt(union node *n);
4440
4441static void
4442cmdlist(union node *np, int sep)
4443{
4444 for (; np; np = np->narg.next) {
4445 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004446 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004447 cmdtxt(np);
4448 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004449 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004450 }
4451}
4452
4453static void
4454cmdtxt(union node *n)
4455{
4456 union node *np;
4457 struct nodelist *lp;
4458 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004459
4460 if (!n)
4461 return;
4462 switch (n->type) {
4463 default:
4464#if DEBUG
4465 abort();
4466#endif
4467 case NPIPE:
4468 lp = n->npipe.cmdlist;
4469 for (;;) {
4470 cmdtxt(lp->n);
4471 lp = lp->next;
4472 if (!lp)
4473 break;
4474 cmdputs(" | ");
4475 }
4476 break;
4477 case NSEMI:
4478 p = "; ";
4479 goto binop;
4480 case NAND:
4481 p = " && ";
4482 goto binop;
4483 case NOR:
4484 p = " || ";
4485 binop:
4486 cmdtxt(n->nbinary.ch1);
4487 cmdputs(p);
4488 n = n->nbinary.ch2;
4489 goto donode;
4490 case NREDIR:
4491 case NBACKGND:
4492 n = n->nredir.n;
4493 goto donode;
4494 case NNOT:
4495 cmdputs("!");
4496 n = n->nnot.com;
4497 donode:
4498 cmdtxt(n);
4499 break;
4500 case NIF:
4501 cmdputs("if ");
4502 cmdtxt(n->nif.test);
4503 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004504 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004505 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004506 cmdputs("; else ");
4507 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004508 } else {
4509 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510 }
4511 p = "; fi";
4512 goto dotail;
4513 case NSUBSHELL:
4514 cmdputs("(");
4515 n = n->nredir.n;
4516 p = ")";
4517 goto dotail;
4518 case NWHILE:
4519 p = "while ";
4520 goto until;
4521 case NUNTIL:
4522 p = "until ";
4523 until:
4524 cmdputs(p);
4525 cmdtxt(n->nbinary.ch1);
4526 n = n->nbinary.ch2;
4527 p = "; done";
4528 dodo:
4529 cmdputs("; do ");
4530 dotail:
4531 cmdtxt(n);
4532 goto dotail2;
4533 case NFOR:
4534 cmdputs("for ");
4535 cmdputs(n->nfor.var);
4536 cmdputs(" in ");
4537 cmdlist(n->nfor.args, 1);
4538 n = n->nfor.body;
4539 p = "; done";
4540 goto dodo;
4541 case NDEFUN:
4542 cmdputs(n->narg.text);
4543 p = "() { ... }";
4544 goto dotail2;
4545 case NCMD:
4546 cmdlist(n->ncmd.args, 1);
4547 cmdlist(n->ncmd.redirect, 0);
4548 break;
4549 case NARG:
4550 p = n->narg.text;
4551 dotail2:
4552 cmdputs(p);
4553 break;
4554 case NHERE:
4555 case NXHERE:
4556 p = "<<...";
4557 goto dotail2;
4558 case NCASE:
4559 cmdputs("case ");
4560 cmdputs(n->ncase.expr->narg.text);
4561 cmdputs(" in ");
4562 for (np = n->ncase.cases; np; np = np->nclist.next) {
4563 cmdtxt(np->nclist.pattern);
4564 cmdputs(") ");
4565 cmdtxt(np->nclist.body);
4566 cmdputs(";; ");
4567 }
4568 p = "esac";
4569 goto dotail2;
4570 case NTO:
4571 p = ">";
4572 goto redir;
4573 case NCLOBBER:
4574 p = ">|";
4575 goto redir;
4576 case NAPPEND:
4577 p = ">>";
4578 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004579#if ENABLE_ASH_BASH_COMPAT
4580 case NTO2:
4581#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004582 case NTOFD:
4583 p = ">&";
4584 goto redir;
4585 case NFROM:
4586 p = "<";
4587 goto redir;
4588 case NFROMFD:
4589 p = "<&";
4590 goto redir;
4591 case NFROMTO:
4592 p = "<>";
4593 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004594 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 cmdputs(p);
4596 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004597 cmdputs(utoa(n->ndup.dupfd));
4598 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004599 }
4600 n = n->nfile.fname;
4601 goto donode;
4602 }
4603}
4604
4605static char *
4606commandtext(union node *n)
4607{
4608 char *name;
4609
4610 STARTSTACKSTR(cmdnextc);
4611 cmdtxt(n);
4612 name = stackblock();
4613 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4614 name, cmdnextc, cmdnextc));
4615 return ckstrdup(name);
4616}
4617#endif /* JOBS */
4618
4619/*
4620 * Fork off a subshell. If we are doing job control, give the subshell its
4621 * own process group. Jp is a job structure that the job is to be added to.
4622 * N is the command that will be evaluated by the child. Both jp and n may
4623 * be NULL. The mode parameter can be one of the following:
4624 * FORK_FG - Fork off a foreground process.
4625 * FORK_BG - Fork off a background process.
4626 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4627 * process group even if job control is on.
4628 *
4629 * When job control is turned off, background processes have their standard
4630 * input redirected to /dev/null (except for the second and later processes
4631 * in a pipeline).
4632 *
4633 * Called with interrupts off.
4634 */
4635/*
4636 * Clear traps on a fork.
4637 */
4638static void
4639clear_traps(void)
4640{
4641 char **tp;
4642
4643 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004644 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004645 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004646 if (trap_ptr == trap)
4647 free(*tp);
4648 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004649 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004650 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004651 setsignal(tp - trap);
4652 INT_ON;
4653 }
4654 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004655 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004657
4658/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004659static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004660
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004661/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004662static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004663forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664{
4665 int oldlvl;
4666
4667 TRACE(("Child shell %d\n", getpid()));
4668 oldlvl = shlvl;
4669 shlvl++;
4670
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004671 /* man bash: "Non-builtin commands run by bash have signal handlers
4672 * set to the values inherited by the shell from its parent".
4673 * Do we do it correctly? */
4674
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004675 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004676
4677 if (mode == FORK_NOJOB /* is it `xxx` ? */
4678 && n && n->type == NCMD /* is it single cmd? */
4679 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004680 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004681 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4682 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4683 ) {
4684 TRACE(("Trap hack\n"));
4685 /* Awful hack for `trap` or $(trap).
4686 *
4687 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4688 * contains an example where "trap" is executed in a subshell:
4689 *
4690 * save_traps=$(trap)
4691 * ...
4692 * eval "$save_traps"
4693 *
4694 * Standard does not say that "trap" in subshell shall print
4695 * parent shell's traps. It only says that its output
4696 * must have suitable form, but then, in the above example
4697 * (which is not supposed to be normative), it implies that.
4698 *
4699 * bash (and probably other shell) does implement it
4700 * (traps are reset to defaults, but "trap" still shows them),
4701 * but as a result, "trap" logic is hopelessly messed up:
4702 *
4703 * # trap
4704 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4705 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4706 * # true | trap <--- trap is in subshell - no output (ditto)
4707 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4708 * trap -- 'echo Ho' SIGWINCH
4709 * # echo `(trap)` <--- in subshell in subshell - output
4710 * trap -- 'echo Ho' SIGWINCH
4711 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4712 * trap -- 'echo Ho' SIGWINCH
4713 *
4714 * The rules when to forget and when to not forget traps
4715 * get really complex and nonsensical.
4716 *
4717 * Our solution: ONLY bare $(trap) or `trap` is special.
4718 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004719 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004720 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004721 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004722 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004723 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004724#if JOBS
4725 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004726 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004727 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4728 pid_t pgrp;
4729
4730 if (jp->nprocs == 0)
4731 pgrp = getpid();
4732 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004733 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004734 /* this can fail because we are doing it in the parent also */
4735 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004736 if (mode == FORK_FG)
4737 xtcsetpgrp(ttyfd, pgrp);
4738 setsignal(SIGTSTP);
4739 setsignal(SIGTTOU);
4740 } else
4741#endif
4742 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004743 /* man bash: "When job control is not in effect,
4744 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004745 ignoresig(SIGINT);
4746 ignoresig(SIGQUIT);
4747 if (jp->nprocs == 0) {
4748 close(0);
4749 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004750 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004751 }
4752 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004753 if (!oldlvl) {
4754 if (iflag) { /* why if iflag only? */
4755 setsignal(SIGINT);
4756 setsignal(SIGTERM);
4757 }
4758 /* man bash:
4759 * "In all cases, bash ignores SIGQUIT. Non-builtin
4760 * commands run by bash have signal handlers
4761 * set to the values inherited by the shell
4762 * from its parent".
4763 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004764 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004765 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004766#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004767 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004768 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004769 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004770 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004771 /* "jobs": we do not want to clear job list for it,
4772 * instead we remove only _its_ own_ job from job list.
4773 * This makes "jobs .... | cat" more useful.
4774 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004775 freejob(curjob);
4776 return;
4777 }
4778#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004779 for (jp = curjob; jp; jp = jp->prev_job)
4780 freejob(jp);
4781 jobless = 0;
4782}
4783
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004784/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004785#if !JOBS
4786#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4787#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004788static void
4789forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4790{
4791 TRACE(("In parent shell: child = %d\n", pid));
4792 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004793 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4794 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004795 jobless++;
4796 return;
4797 }
4798#if JOBS
4799 if (mode != FORK_NOJOB && jp->jobctl) {
4800 int pgrp;
4801
4802 if (jp->nprocs == 0)
4803 pgrp = pid;
4804 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004805 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004806 /* This can fail because we are doing it in the child also */
4807 setpgid(pid, pgrp);
4808 }
4809#endif
4810 if (mode == FORK_BG) {
4811 backgndpid = pid; /* set $! */
4812 set_curjob(jp, CUR_RUNNING);
4813 }
4814 if (jp) {
4815 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004816 ps->ps_pid = pid;
4817 ps->ps_status = -1;
4818 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004819#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004820 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004821 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822#endif
4823 }
4824}
4825
4826static int
4827forkshell(struct job *jp, union node *n, int mode)
4828{
4829 int pid;
4830
4831 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4832 pid = fork();
4833 if (pid < 0) {
4834 TRACE(("Fork failed, errno=%d", errno));
4835 if (jp)
4836 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004837 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004838 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004839 if (pid == 0) {
4840 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004841 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004842 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004843 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004844 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004845 return pid;
4846}
4847
4848/*
4849 * Wait for job to finish.
4850 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004851 * Under job control we have the problem that while a child process
4852 * is running interrupts generated by the user are sent to the child
4853 * but not to the shell. This means that an infinite loop started by
4854 * an interactive user may be hard to kill. With job control turned off,
4855 * an interactive user may place an interactive program inside a loop.
4856 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004857 * these interrupts to also abort the loop. The approach we take here
4858 * is to have the shell ignore interrupt signals while waiting for a
4859 * foreground process to terminate, and then send itself an interrupt
4860 * signal if the child process was terminated by an interrupt signal.
4861 * Unfortunately, some programs want to do a bit of cleanup and then
4862 * exit on interrupt; unless these processes terminate themselves by
4863 * sending a signal to themselves (instead of calling exit) they will
4864 * confuse this approach.
4865 *
4866 * Called with interrupts off.
4867 */
4868static int
4869waitforjob(struct job *jp)
4870{
4871 int st;
4872
4873 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004874
4875 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004876 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004877 /* In non-interactive shells, we _can_ get
4878 * a keyboard signal here and be EINTRed,
4879 * but we just loop back, waiting for command to complete.
4880 *
4881 * man bash:
4882 * "If bash is waiting for a command to complete and receives
4883 * a signal for which a trap has been set, the trap
4884 * will not be executed until the command completes."
4885 *
4886 * Reality is that even if trap is not set, bash
4887 * will not act on the signal until command completes.
4888 * Try this. sleep5intoff.c:
4889 * #include <signal.h>
4890 * #include <unistd.h>
4891 * int main() {
4892 * sigset_t set;
4893 * sigemptyset(&set);
4894 * sigaddset(&set, SIGINT);
4895 * sigaddset(&set, SIGQUIT);
4896 * sigprocmask(SIG_BLOCK, &set, NULL);
4897 * sleep(5);
4898 * return 0;
4899 * }
4900 * $ bash -c './sleep5intoff; echo hi'
4901 * ^C^C^C^C <--- pressing ^C once a second
4902 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004903 * $ bash -c './sleep5intoff; echo hi'
4904 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4905 * $ _
4906 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004907 dowait(DOWAIT_BLOCK, jp);
4908 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004909 INT_ON;
4910
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004911 st = getstatus(jp);
4912#if JOBS
4913 if (jp->jobctl) {
4914 xtcsetpgrp(ttyfd, rootpid);
4915 /*
4916 * This is truly gross.
4917 * If we're doing job control, then we did a TIOCSPGRP which
4918 * caused us (the shell) to no longer be in the controlling
4919 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4920 * intuit from the subprocess exit status whether a SIGINT
4921 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4922 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004923 if (jp->sigint) /* TODO: do the same with all signals */
4924 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004925 }
4926 if (jp->state == JOBDONE)
4927#endif
4928 freejob(jp);
4929 return st;
4930}
4931
4932/*
4933 * return 1 if there are stopped jobs, otherwise 0
4934 */
4935static int
4936stoppedjobs(void)
4937{
4938 struct job *jp;
4939 int retval;
4940
4941 retval = 0;
4942 if (job_warning)
4943 goto out;
4944 jp = curjob;
4945 if (jp && jp->state == JOBSTOPPED) {
4946 out2str("You have stopped jobs.\n");
4947 job_warning = 2;
4948 retval++;
4949 }
4950 out:
4951 return retval;
4952}
4953
4954
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004955/* ============ redir.c
4956 *
4957 * Code for dealing with input/output redirection.
4958 */
4959
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004960#undef EMPTY
4961#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004962#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004963#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004964
4965/*
4966 * Open a file in noclobber mode.
4967 * The code was copied from bash.
4968 */
4969static int
4970noclobberopen(const char *fname)
4971{
4972 int r, fd;
4973 struct stat finfo, finfo2;
4974
4975 /*
4976 * If the file exists and is a regular file, return an error
4977 * immediately.
4978 */
4979 r = stat(fname, &finfo);
4980 if (r == 0 && S_ISREG(finfo.st_mode)) {
4981 errno = EEXIST;
4982 return -1;
4983 }
4984
4985 /*
4986 * If the file was not present (r != 0), make sure we open it
4987 * exclusively so that if it is created before we open it, our open
4988 * will fail. Make sure that we do not truncate an existing file.
4989 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4990 * file was not a regular file, we leave O_EXCL off.
4991 */
4992 if (r != 0)
4993 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4994 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4995
4996 /* If the open failed, return the file descriptor right away. */
4997 if (fd < 0)
4998 return fd;
4999
5000 /*
5001 * OK, the open succeeded, but the file may have been changed from a
5002 * non-regular file to a regular file between the stat and the open.
5003 * We are assuming that the O_EXCL open handles the case where FILENAME
5004 * did not exist and is symlinked to an existing file between the stat
5005 * and open.
5006 */
5007
5008 /*
5009 * If we can open it and fstat the file descriptor, and neither check
5010 * revealed that it was a regular file, and the file has not been
5011 * replaced, return the file descriptor.
5012 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005013 if (fstat(fd, &finfo2) == 0
5014 && !S_ISREG(finfo2.st_mode)
5015 && finfo.st_dev == finfo2.st_dev
5016 && finfo.st_ino == finfo2.st_ino
5017 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005018 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005019 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005020
5021 /* The file has been replaced. badness. */
5022 close(fd);
5023 errno = EEXIST;
5024 return -1;
5025}
5026
5027/*
5028 * Handle here documents. Normally we fork off a process to write the
5029 * data to a pipe. If the document is short, we can stuff the data in
5030 * the pipe without forking.
5031 */
5032/* openhere needs this forward reference */
5033static void expandhere(union node *arg, int fd);
5034static int
5035openhere(union node *redir)
5036{
5037 int pip[2];
5038 size_t len = 0;
5039
5040 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005041 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005042 if (redir->type == NHERE) {
5043 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005044 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 full_write(pip[1], redir->nhere.doc->narg.text, len);
5046 goto out;
5047 }
5048 }
5049 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005050 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005051 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005052 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5053 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5054 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5055 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005056 signal(SIGPIPE, SIG_DFL);
5057 if (redir->type == NHERE)
5058 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005059 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005060 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005061 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005062 }
5063 out:
5064 close(pip[1]);
5065 return pip[0];
5066}
5067
5068static int
5069openredirect(union node *redir)
5070{
5071 char *fname;
5072 int f;
5073
5074 switch (redir->nfile.type) {
5075 case NFROM:
5076 fname = redir->nfile.expfname;
5077 f = open(fname, O_RDONLY);
5078 if (f < 0)
5079 goto eopen;
5080 break;
5081 case NFROMTO:
5082 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005083 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005084 if (f < 0)
5085 goto ecreate;
5086 break;
5087 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005088#if ENABLE_ASH_BASH_COMPAT
5089 case NTO2:
5090#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005091 /* Take care of noclobber mode. */
5092 if (Cflag) {
5093 fname = redir->nfile.expfname;
5094 f = noclobberopen(fname);
5095 if (f < 0)
5096 goto ecreate;
5097 break;
5098 }
5099 /* FALLTHROUGH */
5100 case NCLOBBER:
5101 fname = redir->nfile.expfname;
5102 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5103 if (f < 0)
5104 goto ecreate;
5105 break;
5106 case NAPPEND:
5107 fname = redir->nfile.expfname;
5108 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5109 if (f < 0)
5110 goto ecreate;
5111 break;
5112 default:
5113#if DEBUG
5114 abort();
5115#endif
5116 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005117/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005118// case NTOFD:
5119// case NFROMFD:
5120// f = -1;
5121// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122 case NHERE:
5123 case NXHERE:
5124 f = openhere(redir);
5125 break;
5126 }
5127
5128 return f;
5129 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005130 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005131 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005132 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005133}
5134
5135/*
5136 * Copy a file descriptor to be >= to. Returns -1
5137 * if the source file descriptor is closed, EMPTY if there are no unused
5138 * file descriptors left.
5139 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005140/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5141 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005142enum {
5143 COPYFD_EXACT = (int)~(INT_MAX),
5144 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5145};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005146static int
5147copyfd(int from, int to)
5148{
5149 int newfd;
5150
Denis Vlasenko5a867312008-07-24 19:46:38 +00005151 if (to & COPYFD_EXACT) {
5152 to &= ~COPYFD_EXACT;
5153 /*if (from != to)*/
5154 newfd = dup2(from, to);
5155 } else {
5156 newfd = fcntl(from, F_DUPFD, to);
5157 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005158 if (newfd < 0) {
5159 if (errno == EMFILE)
5160 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005161 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005162 ash_msg_and_raise_error("%d: %m", from);
5163 }
5164 return newfd;
5165}
5166
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005167/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005168struct two_fd_t {
5169 int orig, copy;
5170};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005171struct redirtab {
5172 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005173 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005174 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005175 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005176};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005177#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005178
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005179static int need_to_remember(struct redirtab *rp, int fd)
5180{
5181 int i;
5182
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005183 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005184 return 0;
5185
5186 for (i = 0; i < rp->pair_count; i++) {
5187 if (rp->two_fd[i].orig == fd) {
5188 /* already remembered */
5189 return 0;
5190 }
5191 }
5192 return 1;
5193}
5194
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005195/* "hidden" fd is a fd used to read scripts, or a copy of such */
5196static int is_hidden_fd(struct redirtab *rp, int fd)
5197{
5198 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005199 struct parsefile *pf;
5200
5201 if (fd == -1)
5202 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005203 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005204 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005205 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005206 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005207 * $ ash # running ash interactively
5208 * $ . ./script.sh
5209 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005210 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005211 * it's still ok to use it: "read" builtin uses it,
5212 * why should we cripple "exec" builtin?
5213 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005214 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005215 return 1;
5216 }
5217 pf = pf->prev;
5218 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005219
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005220 if (!rp)
5221 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005222 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005223 fd |= COPYFD_RESTORE;
5224 for (i = 0; i < rp->pair_count; i++) {
5225 if (rp->two_fd[i].copy == fd) {
5226 return 1;
5227 }
5228 }
5229 return 0;
5230}
5231
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005232/*
5233 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5234 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005235 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005236 */
5237/* flags passed to redirect */
5238#define REDIR_PUSH 01 /* save previous values of file descriptors */
5239#define REDIR_SAVEFD2 03 /* set preverrout */
5240static void
5241redirect(union node *redir, int flags)
5242{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005243 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005244 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005245 int i;
5246 int fd;
5247 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005248 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005249
Denis Vlasenko01631112007-12-16 17:20:38 +00005250 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005251 if (!redir) {
5252 return;
5253 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005254
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005255 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005256 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005257 INT_OFF;
5258 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005259 union node *tmp = redir;
5260 do {
5261 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005262#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005263 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005264 sv_pos++;
5265#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005266 tmp = tmp->nfile.next;
5267 } while (tmp);
5268 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005269 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005270 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005271 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005272 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005273 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005274 while (sv_pos > 0) {
5275 sv_pos--;
5276 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5277 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005278 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005279
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005280 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005281 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005282 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005283 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005284 right_fd = redir->ndup.dupfd;
5285 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005286 /* redirect from/to same file descriptor? */
5287 if (right_fd == fd)
5288 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005289 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005290 if (is_hidden_fd(sv, right_fd)) {
5291 errno = EBADF; /* as if it is closed */
5292 ash_msg_and_raise_error("%d: %m", right_fd);
5293 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005294 newfd = -1;
5295 } else {
5296 newfd = openredirect(redir); /* always >= 0 */
5297 if (fd == newfd) {
5298 /* Descriptor wasn't open before redirect.
5299 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005300 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005301 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005302 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005303 continue;
5304 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005305 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005306#if ENABLE_ASH_BASH_COMPAT
5307 redirect_more:
5308#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005309 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005310 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005311 /* Careful to not accidentally "save"
5312 * to the same fd as right side fd in N>&M */
5313 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5314 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005315/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5316 * are closed in popredir() in the child, preventing them from leaking
5317 * into child. (popredir() also cleans up the mess in case of failures)
5318 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005319 if (i == -1) {
5320 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005321 if (i != EBADF) {
5322 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005323 if (newfd >= 0)
5324 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005325 errno = i;
5326 ash_msg_and_raise_error("%d: %m", fd);
5327 /* NOTREACHED */
5328 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005329 /* EBADF: it is not open - good, remember to close it */
5330 remember_to_close:
5331 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005332 } else { /* fd is open, save its copy */
5333 /* "exec fd>&-" should not close fds
5334 * which point to script file(s).
5335 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005336 if (is_hidden_fd(sv, fd))
5337 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005338 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005339 if (fd == 2)
5340 copied_fd2 = i;
5341 sv->two_fd[sv_pos].orig = fd;
5342 sv->two_fd[sv_pos].copy = i;
5343 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005344 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005345 if (newfd < 0) {
5346 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005347 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005348 /* Don't want to trigger debugging */
5349 if (fd != -1)
5350 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005351 } else {
5352 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005353 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005354 } else if (fd != newfd) { /* move newfd to fd */
5355 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005356#if ENABLE_ASH_BASH_COMPAT
5357 if (!(redir->nfile.type == NTO2 && fd == 2))
5358#endif
5359 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005360 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005361#if ENABLE_ASH_BASH_COMPAT
5362 if (redir->nfile.type == NTO2 && fd == 1) {
5363 /* We already redirected it to fd 1, now copy it to 2 */
5364 newfd = 1;
5365 fd = 2;
5366 goto redirect_more;
5367 }
5368#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005369 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005370
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005371 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005372 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5373 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005374}
5375
5376/*
5377 * Undo the effects of the last redirection.
5378 */
5379static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005380popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005381{
5382 struct redirtab *rp;
5383 int i;
5384
Denis Vlasenko01631112007-12-16 17:20:38 +00005385 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005386 return;
5387 INT_OFF;
5388 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005389 for (i = 0; i < rp->pair_count; i++) {
5390 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005391 int copy = rp->two_fd[i].copy;
5392 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005393 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005394 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005395 continue;
5396 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005397 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005398 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005399 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005400 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005401 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005402 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005403 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005404 }
5405 }
5406 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005407 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005408 free(rp);
5409 INT_ON;
5410}
5411
5412/*
5413 * Undo all redirections. Called on error or interrupt.
5414 */
5415
5416/*
5417 * Discard all saved file descriptors.
5418 */
5419static void
5420clearredir(int drop)
5421{
5422 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005423 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005424 if (!redirlist)
5425 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005426 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005427 }
5428}
5429
5430static int
5431redirectsafe(union node *redir, int flags)
5432{
5433 int err;
5434 volatile int saveint;
5435 struct jmploc *volatile savehandler = exception_handler;
5436 struct jmploc jmploc;
5437
5438 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005439 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5440 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005441 if (!err) {
5442 exception_handler = &jmploc;
5443 redirect(redir, flags);
5444 }
5445 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005446 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005447 longjmp(exception_handler->loc, 1);
5448 RESTORE_INT(saveint);
5449 return err;
5450}
5451
5452
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005453/* ============ Routines to expand arguments to commands
5454 *
5455 * We have to deal with backquotes, shell variables, and file metacharacters.
5456 */
5457
Mike Frysinger98c52642009-04-02 10:02:37 +00005458#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005459static arith_t
5460ash_arith(const char *s)
5461{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005462 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005463 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005464
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005465 math_state.lookupvar = lookupvar;
5466 math_state.setvar = setvar2;
5467 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005468
5469 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005470 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005471 if (math_state.errmsg)
5472 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005473 INT_ON;
5474
5475 return result;
5476}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005477#endif
5478
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005479/*
5480 * expandarg flags
5481 */
5482#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5483#define EXP_TILDE 0x2 /* do normal tilde expansion */
5484#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5485#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5486#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5487#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5488#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5489#define EXP_WORD 0x80 /* expand word in parameter expansion */
5490#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5491/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005492 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005493 */
5494#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5495#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5496#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5497#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5498#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5499
5500/*
5501 * Structure specifying which parts of the string should be searched
5502 * for IFS characters.
5503 */
5504struct ifsregion {
5505 struct ifsregion *next; /* next region in list */
5506 int begoff; /* offset of start of region */
5507 int endoff; /* offset of end of region */
5508 int nulonly; /* search for nul bytes only */
5509};
5510
5511struct arglist {
5512 struct strlist *list;
5513 struct strlist **lastp;
5514};
5515
5516/* output of current string */
5517static char *expdest;
5518/* list of back quote expressions */
5519static struct nodelist *argbackq;
5520/* first struct in list of ifs regions */
5521static struct ifsregion ifsfirst;
5522/* last struct in list */
5523static struct ifsregion *ifslastp;
5524/* holds expanded arg list */
5525static struct arglist exparg;
5526
5527/*
5528 * Our own itoa().
5529 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005530#if !ENABLE_SH_MATH_SUPPORT
5531/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5532typedef long arith_t;
5533# define ARITH_FMT "%ld"
5534#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005535static int
5536cvtnum(arith_t num)
5537{
5538 int len;
5539
5540 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005541 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005542 STADJUST(len, expdest);
5543 return len;
5544}
5545
5546static size_t
5547esclen(const char *start, const char *p)
5548{
5549 size_t esc = 0;
5550
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005551 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005552 esc++;
5553 }
5554 return esc;
5555}
5556
5557/*
5558 * Remove any CTLESC characters from a string.
5559 */
5560static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005561rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005562{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005563 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005564
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005565 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005566 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005567 unsigned protect_against_glob;
5568 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569
5570 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005571 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005572 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005573
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005574 q = p;
5575 r = str;
5576 if (flag & RMESCAPE_ALLOC) {
5577 size_t len = p - str;
5578 size_t fulllen = len + strlen(p) + 1;
5579
5580 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005581 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005582 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005583 /* p and str may be invalidated by makestrspace */
5584 str = (char *)stackblock() + strloc;
5585 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005586 } else if (flag & RMESCAPE_HEAP) {
5587 r = ckmalloc(fulllen);
5588 } else {
5589 r = stalloc(fulllen);
5590 }
5591 q = r;
5592 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005593 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005594 }
5595 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005596
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005597 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5598 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005599 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005601 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005602// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5603// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5604// Note: both inquotes and protect_against_glob only affect whether
5605// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 inquotes = ~inquotes;
5607 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005608 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609 continue;
5610 }
5611 if (*p == '\\') {
5612 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005613 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005614 goto copy;
5615 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005616 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005617 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005618 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005619 *q++ = '\\';
5620 }
5621 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005622 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005623 copy:
5624 *q++ = *p++;
5625 }
5626 *q = '\0';
5627 if (flag & RMESCAPE_GROW) {
5628 expdest = r;
5629 STADJUST(q - r + 1, expdest);
5630 }
5631 return r;
5632}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005633#define pmatch(a, b) !fnmatch((a), (b), 0)
5634
5635/*
5636 * Prepare a pattern for a expmeta (internal glob(3)) call.
5637 *
5638 * Returns an stalloced string.
5639 */
5640static char *
5641preglob(const char *pattern, int quoted, int flag)
5642{
5643 flag |= RMESCAPE_GLOB;
5644 if (quoted) {
5645 flag |= RMESCAPE_QUOTED;
5646 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005647 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648}
5649
5650/*
5651 * Put a string on the stack.
5652 */
5653static void
5654memtodest(const char *p, size_t len, int syntax, int quotes)
5655{
5656 char *q = expdest;
5657
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005658 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005659
5660 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005661 unsigned char c = *p++;
5662 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005663 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005664 if (quotes) {
5665 int n = SIT(c, syntax);
5666 if (n == CCTL || n == CBACK)
5667 USTPUTC(CTLESC, q);
5668 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005669 USTPUTC(c, q);
5670 }
5671
5672 expdest = q;
5673}
5674
5675static void
5676strtodest(const char *p, int syntax, int quotes)
5677{
5678 memtodest(p, strlen(p), syntax, quotes);
5679}
5680
5681/*
5682 * Record the fact that we have to scan this region of the
5683 * string for IFS characters.
5684 */
5685static void
5686recordregion(int start, int end, int nulonly)
5687{
5688 struct ifsregion *ifsp;
5689
5690 if (ifslastp == NULL) {
5691 ifsp = &ifsfirst;
5692 } else {
5693 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005694 ifsp = ckzalloc(sizeof(*ifsp));
5695 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005696 ifslastp->next = ifsp;
5697 INT_ON;
5698 }
5699 ifslastp = ifsp;
5700 ifslastp->begoff = start;
5701 ifslastp->endoff = end;
5702 ifslastp->nulonly = nulonly;
5703}
5704
5705static void
5706removerecordregions(int endoff)
5707{
5708 if (ifslastp == NULL)
5709 return;
5710
5711 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005712 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005713 struct ifsregion *ifsp;
5714 INT_OFF;
5715 ifsp = ifsfirst.next->next;
5716 free(ifsfirst.next);
5717 ifsfirst.next = ifsp;
5718 INT_ON;
5719 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005720 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005721 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005722 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005723 ifslastp = &ifsfirst;
5724 ifsfirst.endoff = endoff;
5725 }
5726 return;
5727 }
5728
5729 ifslastp = &ifsfirst;
5730 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005731 ifslastp = ifslastp->next;
5732 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005733 struct ifsregion *ifsp;
5734 INT_OFF;
5735 ifsp = ifslastp->next->next;
5736 free(ifslastp->next);
5737 ifslastp->next = ifsp;
5738 INT_ON;
5739 }
5740 if (ifslastp->endoff > endoff)
5741 ifslastp->endoff = endoff;
5742}
5743
5744static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005745exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005746{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005747 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005748 char *name;
5749 struct passwd *pw;
5750 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005751 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005752 int startloc;
5753
5754 name = p + 1;
5755
5756 while ((c = *++p) != '\0') {
5757 switch (c) {
5758 case CTLESC:
5759 return startp;
5760 case CTLQUOTEMARK:
5761 return startp;
5762 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005763 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005764 goto done;
5765 break;
5766 case '/':
5767 case CTLENDVAR:
5768 goto done;
5769 }
5770 }
5771 done:
5772 *p = '\0';
5773 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005774 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005775 } else {
5776 pw = getpwnam(name);
5777 if (pw == NULL)
5778 goto lose;
5779 home = pw->pw_dir;
5780 }
5781 if (!home || !*home)
5782 goto lose;
5783 *p = c;
5784 startloc = expdest - (char *)stackblock();
5785 strtodest(home, SQSYNTAX, quotes);
5786 recordregion(startloc, expdest - (char *)stackblock(), 0);
5787 return p;
5788 lose:
5789 *p = c;
5790 return startp;
5791}
5792
5793/*
5794 * Execute a command inside back quotes. If it's a builtin command, we
5795 * want to save its output in a block obtained from malloc. Otherwise
5796 * we fork off a subprocess and get the output of the command via a pipe.
5797 * Should be called with interrupts off.
5798 */
5799struct backcmd { /* result of evalbackcmd */
5800 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005801 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005802 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005803 struct job *jp; /* job structure for command */
5804};
5805
5806/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005807static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005808#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005809static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005811static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005812evalbackcmd(union node *n, struct backcmd *result)
5813{
5814 int saveherefd;
5815
5816 result->fd = -1;
5817 result->buf = NULL;
5818 result->nleft = 0;
5819 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005820 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005821 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005822
5823 saveherefd = herefd;
5824 herefd = -1;
5825
5826 {
5827 int pip[2];
5828 struct job *jp;
5829
5830 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005831 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005832 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005833 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5834 FORCE_INT_ON;
5835 close(pip[0]);
5836 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005837 /*close(1);*/
5838 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839 close(pip[1]);
5840 }
5841 eflag = 0;
5842 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5843 /* NOTREACHED */
5844 }
5845 close(pip[1]);
5846 result->fd = pip[0];
5847 result->jp = jp;
5848 }
5849 herefd = saveherefd;
5850 out:
5851 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5852 result->fd, result->buf, result->nleft, result->jp));
5853}
5854
5855/*
5856 * Expand stuff in backwards quotes.
5857 */
5858static void
5859expbackq(union node *cmd, int quoted, int quotes)
5860{
5861 struct backcmd in;
5862 int i;
5863 char buf[128];
5864 char *p;
5865 char *dest;
5866 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005867 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 struct stackmark smark;
5869
5870 INT_OFF;
5871 setstackmark(&smark);
5872 dest = expdest;
5873 startloc = dest - (char *)stackblock();
5874 grabstackstr(dest);
5875 evalbackcmd(cmd, &in);
5876 popstackmark(&smark);
5877
5878 p = in.buf;
5879 i = in.nleft;
5880 if (i == 0)
5881 goto read;
5882 for (;;) {
5883 memtodest(p, i, syntax, quotes);
5884 read:
5885 if (in.fd < 0)
5886 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005887 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005888 TRACE(("expbackq: read returns %d\n", i));
5889 if (i <= 0)
5890 break;
5891 p = buf;
5892 }
5893
Denis Vlasenko60818682007-09-28 22:07:23 +00005894 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005895 if (in.fd >= 0) {
5896 close(in.fd);
5897 back_exitstatus = waitforjob(in.jp);
5898 }
5899 INT_ON;
5900
5901 /* Eat all trailing newlines */
5902 dest = expdest;
5903 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5904 STUNPUTC(dest);
5905 expdest = dest;
5906
5907 if (quoted == 0)
5908 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005909 TRACE(("evalbackq: size:%d:'%.*s'\n",
5910 (int)((dest - (char *)stackblock()) - startloc),
5911 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005912 stackblock() + startloc));
5913}
5914
Mike Frysinger98c52642009-04-02 10:02:37 +00005915#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005916/*
5917 * Expand arithmetic expression. Backup to start of expression,
5918 * evaluate, place result in (backed up) result, adjust string position.
5919 */
5920static void
5921expari(int quotes)
5922{
5923 char *p, *start;
5924 int begoff;
5925 int flag;
5926 int len;
5927
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005928 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005929
5930 /*
5931 * This routine is slightly over-complicated for
5932 * efficiency. Next we scan backwards looking for the
5933 * start of arithmetic.
5934 */
5935 start = stackblock();
5936 p = expdest - 1;
5937 *p = '\0';
5938 p--;
5939 do {
5940 int esc;
5941
Denys Vlasenkocd716832009-11-28 22:14:02 +01005942 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005943 p--;
5944#if DEBUG
5945 if (p < start) {
5946 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5947 }
5948#endif
5949 }
5950
5951 esc = esclen(start, p);
5952 if (!(esc % 2)) {
5953 break;
5954 }
5955
5956 p -= esc + 1;
5957 } while (1);
5958
5959 begoff = p - start;
5960
5961 removerecordregions(begoff);
5962
5963 flag = p[1];
5964
5965 expdest = p;
5966
5967 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005968 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005969
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005970 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005971
5972 if (flag != '"')
5973 recordregion(begoff, begoff + len, 0);
5974}
5975#endif
5976
5977/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005978static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005979
5980/*
5981 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5982 * characters to allow for further processing. Otherwise treat
5983 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005984 *
5985 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5986 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5987 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005988 */
5989static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005990argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005991{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005992 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005993 '=',
5994 ':',
5995 CTLQUOTEMARK,
5996 CTLENDVAR,
5997 CTLESC,
5998 CTLVAR,
5999 CTLBACKQ,
6000 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006001#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006002 CTLENDARI,
6003#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006004 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005 };
6006 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006007 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6008 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006009 int inquotes;
6010 size_t length;
6011 int startloc;
6012
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006013 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006014 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006015 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006016 reject++;
6017 }
6018 inquotes = 0;
6019 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006020 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006021 char *q;
6022
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006023 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006024 tilde:
6025 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006026 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 q++;
6028 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006029 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006030 }
6031 start:
6032 startloc = expdest - (char *)stackblock();
6033 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006034 unsigned char c;
6035
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006037 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006038 if (c) {
6039 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006040 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006041 ) {
6042 /* c == '=' || c == ':' || c == CTLENDARI */
6043 length++;
6044 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 }
6046 if (length > 0) {
6047 int newloc;
6048 expdest = stack_nputstr(p, length, expdest);
6049 newloc = expdest - (char *)stackblock();
6050 if (breakall && !inquotes && newloc > startloc) {
6051 recordregion(startloc, newloc, 0);
6052 }
6053 startloc = newloc;
6054 }
6055 p += length + 1;
6056 length = 0;
6057
6058 switch (c) {
6059 case '\0':
6060 goto breakloop;
6061 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006062 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 p--;
6064 continue;
6065 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006066 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006067 reject++;
6068 /* fall through */
6069 case ':':
6070 /*
6071 * sort of a hack - expand tildes in variable
6072 * assignments (after the first '=' and after ':'s).
6073 */
6074 if (*--p == '~') {
6075 goto tilde;
6076 }
6077 continue;
6078 }
6079
6080 switch (c) {
6081 case CTLENDVAR: /* ??? */
6082 goto breakloop;
6083 case CTLQUOTEMARK:
6084 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006085 if (!inquotes
6086 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006087 && ( p[4] == (char)CTLQUOTEMARK
6088 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006089 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006091 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006092 goto start;
6093 }
6094 inquotes = !inquotes;
6095 addquote:
6096 if (quotes) {
6097 p--;
6098 length++;
6099 startloc++;
6100 }
6101 break;
6102 case CTLESC:
6103 startloc++;
6104 length++;
6105 goto addquote;
6106 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006107 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006108 goto start;
6109 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006110 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006111 case CTLBACKQ|CTLQUOTE:
6112 expbackq(argbackq->n, c, quotes);
6113 argbackq = argbackq->next;
6114 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006115#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006116 case CTLENDARI:
6117 p--;
6118 expari(quotes);
6119 goto start;
6120#endif
6121 }
6122 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006123 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124}
6125
6126static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006127scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6128 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006129{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006130 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006131 char c;
6132
6133 loc = startp;
6134 loc2 = rmesc;
6135 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006136 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006138
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006139 c = *loc2;
6140 if (zero) {
6141 *loc2 = '\0';
6142 s = rmesc;
6143 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006144 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006145
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006146 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006147 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006148 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006149 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006150 loc++;
6151 loc++;
6152 loc2++;
6153 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006154 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006155}
6156
6157static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006158scanright(char *startp, char *rmesc, char *rmescend,
6159 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006160{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006161#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6162 int try2optimize = match_at_start;
6163#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006164 int esc = 0;
6165 char *loc;
6166 char *loc2;
6167
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006168 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6169 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6170 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6171 * Logic:
6172 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6173 * and on each iteration they go back two/one char until they reach the beginning.
6174 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6175 */
6176 /* TODO: document in what other circumstances we are called. */
6177
6178 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006179 int match;
6180 char c = *loc2;
6181 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006182 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 *loc2 = '\0';
6184 s = rmesc;
6185 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006186 match = pmatch(pattern, s);
6187 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006188 *loc2 = c;
6189 if (match)
6190 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006191#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6192 if (try2optimize) {
6193 /* Maybe we can optimize this:
6194 * if pattern ends with unescaped *, we can avoid checking
6195 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6196 * it wont match truncated "raw_value_of_" strings too.
6197 */
6198 unsigned plen = strlen(pattern);
6199 /* Does it end with "*"? */
6200 if (plen != 0 && pattern[--plen] == '*') {
6201 /* "xxxx*" is not escaped */
6202 /* "xxx\*" is escaped */
6203 /* "xx\\*" is not escaped */
6204 /* "x\\\*" is escaped */
6205 int slashes = 0;
6206 while (plen != 0 && pattern[--plen] == '\\')
6207 slashes++;
6208 if (!(slashes & 1))
6209 break; /* ends with unescaped "*" */
6210 }
6211 try2optimize = 0;
6212 }
6213#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006214 loc--;
6215 if (quotes) {
6216 if (--esc < 0) {
6217 esc = esclen(startp, loc);
6218 }
6219 if (esc % 2) {
6220 esc--;
6221 loc--;
6222 }
6223 }
6224 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006225 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006226}
6227
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006228static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006229static void
6230varunset(const char *end, const char *var, const char *umsg, int varflags)
6231{
6232 const char *msg;
6233 const char *tail;
6234
6235 tail = nullstr;
6236 msg = "parameter not set";
6237 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006238 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006239 if (varflags & VSNUL)
6240 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006241 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006242 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006243 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006244 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006245 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006246}
6247
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006248#if ENABLE_ASH_BASH_COMPAT
6249static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006250parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006251{
6252 char *idx, *repl = NULL;
6253 unsigned char c;
6254
Denys Vlasenko16149002010-08-06 22:06:21 +02006255 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006256 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006257 idx = arg;
6258 while (1) {
6259 c = *arg;
6260 if (!c)
6261 break;
6262 if (c == '/') {
6263 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006264 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006265 repl = idx + 1;
6266 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006267 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006268 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006269 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006270 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006271 /*
6272 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6273 * The result is a_\_z_c (not a\_\_z_c)!
6274 *
6275 * Enable debug prints in this function and you'll see:
6276 * ash: arg:'\\b/_\\_z_' varflags:d
6277 * ash: pattern:'\\b' repl:'_\_z_'
6278 * That is, \\b is interpreted as \\b, but \\_ as \_!
6279 * IOW: search pattern and replace string treat backslashes
6280 * differently! That is the reason why we check repl below:
6281 */
6282 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6283 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006285 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006286 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006287
6288 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006289}
6290#endif /* ENABLE_ASH_BASH_COMPAT */
6291
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006292static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006293subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006294 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006295{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006296 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006297 char *startp;
6298 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006299 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006300 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006301 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006302 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006303 int saveherefd = herefd;
6304 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006305 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006306 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006307
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006308 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6309 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006310
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006311 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006312 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6313 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006314 STPUTC('\0', expdest);
6315 herefd = saveherefd;
6316 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006317 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006318
6319 switch (subtype) {
6320 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006321 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322 amount = startp - expdest;
6323 STADJUST(amount, expdest);
6324 return startp;
6325
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006326 case VSQUESTION:
6327 varunset(p, varname, startp, varflags);
6328 /* NOTREACHED */
6329
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006330#if ENABLE_ASH_BASH_COMPAT
6331 case VSSUBSTR:
6332 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006333 /* Read POS in ${var:POS:LEN} */
6334 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006335 len = str - startp - 1;
6336
6337 /* *loc != '\0', guaranteed by parser */
6338 if (quotes) {
6339 char *ptr;
6340
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006341 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006342 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006343 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006344 len--;
6345 ptr++;
6346 }
6347 }
6348 }
6349 orig_len = len;
6350
6351 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006352 /* ${var::LEN} */
6353 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006354 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006355 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006356 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006357 while (*loc && *loc != ':') {
6358 /* TODO?
6359 * bash complains on: var=qwe; echo ${var:1a:123}
6360 if (!isdigit(*loc))
6361 ash_msg_and_raise_error(msg_illnum, str);
6362 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006363 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006364 }
6365 if (*loc++ == ':') {
6366 len = number(loc);
6367 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006368 }
6369 if (pos >= orig_len) {
6370 pos = 0;
6371 len = 0;
6372 }
6373 if (len > (orig_len - pos))
6374 len = orig_len - pos;
6375
6376 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006377 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006378 str++;
6379 }
6380 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006381 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006382 *loc++ = *str++;
6383 *loc++ = *str++;
6384 }
6385 *loc = '\0';
6386 amount = loc - expdest;
6387 STADJUST(amount, expdest);
6388 return loc;
6389#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006390 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006391
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006392 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006393
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006394 /* We'll comeback here if we grow the stack while handling
6395 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6396 * stack will need rebasing, and we'll need to remove our work
6397 * areas each time
6398 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006399 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006400
6401 amount = expdest - ((char *)stackblock() + resetloc);
6402 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006403 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404
6405 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006406 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006407 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006408 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006409 if (rmesc != startp) {
6410 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006411 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006412 }
6413 }
6414 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006415 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006416 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006417 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006418
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006419#if ENABLE_ASH_BASH_COMPAT
6420 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006421 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006422
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006423 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006424 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006425 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006427 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 }
6429
6430 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006431 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006432 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006433
6434 len = 0;
6435 idx = startp;
6436 end = str - 1;
6437 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006438 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006439 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006440 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006441 if (!loc) {
6442 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006443 char *restart_detect = stackblock();
6444 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006445 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006446 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006447 idx++;
6448 len++;
6449 STPUTC(*idx, expdest);
6450 }
6451 if (stackblock() != restart_detect)
6452 goto restart;
6453 idx++;
6454 len++;
6455 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006456 /* continue; - prone to quadratic behavior, smarter code: */
6457 if (idx >= end)
6458 break;
6459 if (str[0] == '*') {
6460 /* Pattern is "*foo". If "*foo" does not match "long_string",
6461 * it would never match "ong_string" etc, no point in trying.
6462 */
6463 goto skip_matching;
6464 }
6465 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006466 }
6467
6468 if (subtype == VSREPLACEALL) {
6469 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006470 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006471 idx++;
6472 idx++;
6473 rmesc++;
6474 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006475 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006476 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006477 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006478
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006479 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006480 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006481 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006482 if (quotes && *loc == '\\') {
6483 STPUTC(CTLESC, expdest);
6484 len++;
6485 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006486 STPUTC(*loc, expdest);
6487 if (stackblock() != restart_detect)
6488 goto restart;
6489 len++;
6490 }
6491
6492 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006493 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006494 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006495 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006496 STPUTC(*idx, expdest);
6497 if (stackblock() != restart_detect)
6498 goto restart;
6499 len++;
6500 idx++;
6501 }
6502 break;
6503 }
6504 }
6505
6506 /* We've put the replaced text into a buffer at workloc, now
6507 * move it to the right place and adjust the stack.
6508 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006509 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006510 startp = (char *)stackblock() + startloc;
6511 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006512 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006513 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006514 STADJUST(-amount, expdest);
6515 return startp;
6516 }
6517#endif /* ENABLE_ASH_BASH_COMPAT */
6518
6519 subtype -= VSTRIMRIGHT;
6520#if DEBUG
6521 if (subtype < 0 || subtype > 7)
6522 abort();
6523#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006524 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006525 zero = subtype >> 1;
6526 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6527 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6528
6529 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6530 if (loc) {
6531 if (zero) {
6532 memmove(startp, loc, str - loc);
6533 loc = startp + (str - loc) - 1;
6534 }
6535 *loc = '\0';
6536 amount = loc - expdest;
6537 STADJUST(amount, expdest);
6538 }
6539 return loc;
6540}
6541
6542/*
6543 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006544 * name parameter (examples):
6545 * ash -c 'echo $1' name:'1='
6546 * ash -c 'echo $qwe' name:'qwe='
6547 * ash -c 'echo $$' name:'$='
6548 * ash -c 'echo ${$}' name:'$='
6549 * ash -c 'echo ${$##q}' name:'$=q'
6550 * ash -c 'echo ${#$}' name:'$='
6551 * note: examples with bad shell syntax:
6552 * ash -c 'echo ${#$1}' name:'$=1'
6553 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006554 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006555static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006556varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006557{
Mike Frysinger98c52642009-04-02 10:02:37 +00006558 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006559 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006560 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006561 int sepq = 0;
6562 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006563 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006564 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006565 int quoted = varflags & VSQUOTE;
6566 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006567
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006568 switch (*name) {
6569 case '$':
6570 num = rootpid;
6571 goto numvar;
6572 case '?':
6573 num = exitstatus;
6574 goto numvar;
6575 case '#':
6576 num = shellparam.nparam;
6577 goto numvar;
6578 case '!':
6579 num = backgndpid;
6580 if (num == 0)
6581 return -1;
6582 numvar:
6583 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006584 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006585 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006586 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006587 for (i = NOPTS - 1; i >= 0; i--) {
6588 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006589 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006590 len++;
6591 }
6592 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006593 check_1char_name:
6594#if 0
6595 /* handles cases similar to ${#$1} */
6596 if (name[2] != '\0')
6597 raise_error_syntax("bad substitution");
6598#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006599 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006600 case '@': {
6601 char **ap;
6602 int sep;
6603
6604 if (quoted && (flags & EXP_FULL)) {
6605 /* note: this is not meant as PEOF value */
6606 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006607 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006608 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006609 /* fall through */
6610 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006611 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006612 i = SIT(sep, syntax);
6613 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006614 sepq = 1;
6615 param:
6616 ap = shellparam.p;
6617 if (!ap)
6618 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006619 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006620 size_t partlen;
6621
6622 partlen = strlen(p);
6623 len += partlen;
6624
6625 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6626 memtodest(p, partlen, syntax, quotes);
6627
6628 if (*ap && sep) {
6629 char *q;
6630
6631 len++;
6632 if (subtype == VSPLUS || subtype == VSLENGTH) {
6633 continue;
6634 }
6635 q = expdest;
6636 if (sepq)
6637 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006638 /* note: may put NUL despite sep != 0
6639 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006640 STPUTC(sep, q);
6641 expdest = q;
6642 }
6643 }
6644 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006645 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006646 case '0':
6647 case '1':
6648 case '2':
6649 case '3':
6650 case '4':
6651 case '5':
6652 case '6':
6653 case '7':
6654 case '8':
6655 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006656 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006657 if (num < 0 || num > shellparam.nparam)
6658 return -1;
6659 p = num ? shellparam.p[num - 1] : arg0;
6660 goto value;
6661 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006662 /* NB: name has form "VAR=..." */
6663
6664 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6665 * which should be considered before we check variables. */
6666 if (var_str_list) {
6667 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6668 p = NULL;
6669 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006670 char *str, *eq;
6671 str = var_str_list->text;
6672 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006673 if (!eq) /* stop at first non-assignment */
6674 break;
6675 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006676 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006677 && strncmp(str, name, name_len) == 0
6678 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006679 p = eq;
6680 /* goto value; - WRONG! */
6681 /* think "A=1 A=2 B=$A" */
6682 }
6683 var_str_list = var_str_list->next;
6684 } while (var_str_list);
6685 if (p)
6686 goto value;
6687 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006688 p = lookupvar(name);
6689 value:
6690 if (!p)
6691 return -1;
6692
6693 len = strlen(p);
6694 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6695 memtodest(p, len, syntax, quotes);
6696 return len;
6697 }
6698
6699 if (subtype == VSPLUS || subtype == VSLENGTH)
6700 STADJUST(-len, expdest);
6701 return len;
6702}
6703
6704/*
6705 * Expand a variable, and return a pointer to the next character in the
6706 * input string.
6707 */
6708static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006709evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006710{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006711 char varflags;
6712 char subtype;
6713 char quoted;
6714 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006715 char *var;
6716 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006717 int startloc;
6718 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006719
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006720 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006721 subtype = varflags & VSTYPE;
6722 quoted = varflags & VSQUOTE;
6723 var = p;
6724 easy = (!quoted || (*var == '@' && shellparam.nparam));
6725 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006726 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006727
6728 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006729 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006730 if (varflags & VSNUL)
6731 varlen--;
6732
6733 if (subtype == VSPLUS) {
6734 varlen = -1 - varlen;
6735 goto vsplus;
6736 }
6737
6738 if (subtype == VSMINUS) {
6739 vsplus:
6740 if (varlen < 0) {
6741 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006742 p,
6743 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006744 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006745 );
6746 goto end;
6747 }
6748 if (easy)
6749 goto record;
6750 goto end;
6751 }
6752
6753 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6754 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006755 if (subevalvar(p, var, /* strloc: */ 0,
6756 subtype, startloc, varflags,
6757 /* quotes: */ 0,
6758 var_str_list)
6759 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006760 varflags &= ~VSNUL;
6761 /*
6762 * Remove any recorded regions beyond
6763 * start of variable
6764 */
6765 removerecordregions(startloc);
6766 goto again;
6767 }
6768 goto end;
6769 }
6770 if (easy)
6771 goto record;
6772 goto end;
6773 }
6774
6775 if (varlen < 0 && uflag)
6776 varunset(p, var, 0, 0);
6777
6778 if (subtype == VSLENGTH) {
6779 cvtnum(varlen > 0 ? varlen : 0);
6780 goto record;
6781 }
6782
6783 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006784 if (easy)
6785 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006786 goto end;
6787 }
6788
6789#if DEBUG
6790 switch (subtype) {
6791 case VSTRIMLEFT:
6792 case VSTRIMLEFTMAX:
6793 case VSTRIMRIGHT:
6794 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006795#if ENABLE_ASH_BASH_COMPAT
6796 case VSSUBSTR:
6797 case VSREPLACE:
6798 case VSREPLACEALL:
6799#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006800 break;
6801 default:
6802 abort();
6803 }
6804#endif
6805
6806 if (varlen >= 0) {
6807 /*
6808 * Terminate the string and start recording the pattern
6809 * right after it
6810 */
6811 STPUTC('\0', expdest);
6812 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006813 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006814 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006815//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006816 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006817 var_str_list)
6818 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006819 int amount = expdest - (
6820 (char *)stackblock() + patloc - 1
6821 );
6822 STADJUST(-amount, expdest);
6823 }
6824 /* Remove any recorded regions beyond start of variable */
6825 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006826 record:
6827 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006828 }
6829
6830 end:
6831 if (subtype != VSNORMAL) { /* skip to end of alternative */
6832 int nesting = 1;
6833 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006834 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006835 if (c == CTLESC)
6836 p++;
6837 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6838 if (varlen >= 0)
6839 argbackq = argbackq->next;
6840 } else if (c == CTLVAR) {
6841 if ((*p++ & VSTYPE) != VSNORMAL)
6842 nesting++;
6843 } else if (c == CTLENDVAR) {
6844 if (--nesting == 0)
6845 break;
6846 }
6847 }
6848 }
6849 return p;
6850}
6851
6852/*
6853 * Break the argument string into pieces based upon IFS and add the
6854 * strings to the argument list. The regions of the string to be
6855 * searched for IFS characters have been stored by recordregion.
6856 */
6857static void
6858ifsbreakup(char *string, struct arglist *arglist)
6859{
6860 struct ifsregion *ifsp;
6861 struct strlist *sp;
6862 char *start;
6863 char *p;
6864 char *q;
6865 const char *ifs, *realifs;
6866 int ifsspc;
6867 int nulonly;
6868
6869 start = string;
6870 if (ifslastp != NULL) {
6871 ifsspc = 0;
6872 nulonly = 0;
6873 realifs = ifsset() ? ifsval() : defifs;
6874 ifsp = &ifsfirst;
6875 do {
6876 p = string + ifsp->begoff;
6877 nulonly = ifsp->nulonly;
6878 ifs = nulonly ? nullstr : realifs;
6879 ifsspc = 0;
6880 while (p < string + ifsp->endoff) {
6881 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006882 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006883 p++;
6884 if (!strchr(ifs, *p)) {
6885 p++;
6886 continue;
6887 }
6888 if (!nulonly)
6889 ifsspc = (strchr(defifs, *p) != NULL);
6890 /* Ignore IFS whitespace at start */
6891 if (q == start && ifsspc) {
6892 p++;
6893 start = p;
6894 continue;
6895 }
6896 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006897 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006898 sp->text = start;
6899 *arglist->lastp = sp;
6900 arglist->lastp = &sp->next;
6901 p++;
6902 if (!nulonly) {
6903 for (;;) {
6904 if (p >= string + ifsp->endoff) {
6905 break;
6906 }
6907 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006908 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006909 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006910 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006911 p = q;
6912 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006913 }
6914 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006915 if (ifsspc) {
6916 p++;
6917 ifsspc = 0;
6918 } else {
6919 p = q;
6920 break;
6921 }
6922 } else
6923 p++;
6924 }
6925 }
6926 start = p;
6927 } /* while */
6928 ifsp = ifsp->next;
6929 } while (ifsp != NULL);
6930 if (nulonly)
6931 goto add;
6932 }
6933
6934 if (!*start)
6935 return;
6936
6937 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006938 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006939 sp->text = start;
6940 *arglist->lastp = sp;
6941 arglist->lastp = &sp->next;
6942}
6943
6944static void
6945ifsfree(void)
6946{
6947 struct ifsregion *p;
6948
6949 INT_OFF;
6950 p = ifsfirst.next;
6951 do {
6952 struct ifsregion *ifsp;
6953 ifsp = p->next;
6954 free(p);
6955 p = ifsp;
6956 } while (p);
6957 ifslastp = NULL;
6958 ifsfirst.next = NULL;
6959 INT_ON;
6960}
6961
6962/*
6963 * Add a file name to the list.
6964 */
6965static void
6966addfname(const char *name)
6967{
6968 struct strlist *sp;
6969
Denis Vlasenko597906c2008-02-20 16:38:54 +00006970 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006971 sp->text = ststrdup(name);
6972 *exparg.lastp = sp;
6973 exparg.lastp = &sp->next;
6974}
6975
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006976/*
6977 * Do metacharacter (i.e. *, ?, [...]) expansion.
6978 */
6979static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006980expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006981{
6982 char *p;
6983 const char *cp;
6984 char *start;
6985 char *endname;
6986 int metaflag;
6987 struct stat statb;
6988 DIR *dirp;
6989 struct dirent *dp;
6990 int atend;
6991 int matchdot;
6992
6993 metaflag = 0;
6994 start = name;
6995 for (p = name; *p; p++) {
6996 if (*p == '*' || *p == '?')
6997 metaflag = 1;
6998 else if (*p == '[') {
6999 char *q = p + 1;
7000 if (*q == '!')
7001 q++;
7002 for (;;) {
7003 if (*q == '\\')
7004 q++;
7005 if (*q == '/' || *q == '\0')
7006 break;
7007 if (*++q == ']') {
7008 metaflag = 1;
7009 break;
7010 }
7011 }
7012 } else if (*p == '\\')
7013 p++;
7014 else if (*p == '/') {
7015 if (metaflag)
7016 goto out;
7017 start = p + 1;
7018 }
7019 }
7020 out:
7021 if (metaflag == 0) { /* we've reached the end of the file name */
7022 if (enddir != expdir)
7023 metaflag++;
7024 p = name;
7025 do {
7026 if (*p == '\\')
7027 p++;
7028 *enddir++ = *p;
7029 } while (*p++);
7030 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7031 addfname(expdir);
7032 return;
7033 }
7034 endname = p;
7035 if (name < start) {
7036 p = name;
7037 do {
7038 if (*p == '\\')
7039 p++;
7040 *enddir++ = *p++;
7041 } while (p < start);
7042 }
7043 if (enddir == expdir) {
7044 cp = ".";
7045 } else if (enddir == expdir + 1 && *expdir == '/') {
7046 cp = "/";
7047 } else {
7048 cp = expdir;
7049 enddir[-1] = '\0';
7050 }
7051 dirp = opendir(cp);
7052 if (dirp == NULL)
7053 return;
7054 if (enddir != expdir)
7055 enddir[-1] = '/';
7056 if (*endname == 0) {
7057 atend = 1;
7058 } else {
7059 atend = 0;
7060 *endname++ = '\0';
7061 }
7062 matchdot = 0;
7063 p = start;
7064 if (*p == '\\')
7065 p++;
7066 if (*p == '.')
7067 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007068 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007069 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007070 continue;
7071 if (pmatch(start, dp->d_name)) {
7072 if (atend) {
7073 strcpy(enddir, dp->d_name);
7074 addfname(expdir);
7075 } else {
7076 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7077 continue;
7078 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007079 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007080 }
7081 }
7082 }
7083 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007084 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007085 endname[-1] = '/';
7086}
7087
7088static struct strlist *
7089msort(struct strlist *list, int len)
7090{
7091 struct strlist *p, *q = NULL;
7092 struct strlist **lpp;
7093 int half;
7094 int n;
7095
7096 if (len <= 1)
7097 return list;
7098 half = len >> 1;
7099 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007100 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007101 q = p;
7102 p = p->next;
7103 }
7104 q->next = NULL; /* terminate first half of list */
7105 q = msort(list, half); /* sort first half of list */
7106 p = msort(p, len - half); /* sort second half */
7107 lpp = &list;
7108 for (;;) {
7109#if ENABLE_LOCALE_SUPPORT
7110 if (strcoll(p->text, q->text) < 0)
7111#else
7112 if (strcmp(p->text, q->text) < 0)
7113#endif
7114 {
7115 *lpp = p;
7116 lpp = &p->next;
7117 p = *lpp;
7118 if (p == NULL) {
7119 *lpp = q;
7120 break;
7121 }
7122 } else {
7123 *lpp = q;
7124 lpp = &q->next;
7125 q = *lpp;
7126 if (q == NULL) {
7127 *lpp = p;
7128 break;
7129 }
7130 }
7131 }
7132 return list;
7133}
7134
7135/*
7136 * Sort the results of file name expansion. It calculates the number of
7137 * strings to sort and then calls msort (short for merge sort) to do the
7138 * work.
7139 */
7140static struct strlist *
7141expsort(struct strlist *str)
7142{
7143 int len;
7144 struct strlist *sp;
7145
7146 len = 0;
7147 for (sp = str; sp; sp = sp->next)
7148 len++;
7149 return msort(str, len);
7150}
7151
7152static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007153expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007154{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007155 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007156 '*', '?', '[', 0
7157 };
7158 /* TODO - EXP_REDIR */
7159
7160 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007161 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007162 struct strlist **savelastp;
7163 struct strlist *sp;
7164 char *p;
7165
7166 if (fflag)
7167 goto nometa;
7168 if (!strpbrk(str->text, metachars))
7169 goto nometa;
7170 savelastp = exparg.lastp;
7171
7172 INT_OFF;
7173 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7174 {
7175 int i = strlen(str->text);
7176 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7177 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007178 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007179 free(expdir);
7180 if (p != str->text)
7181 free(p);
7182 INT_ON;
7183 if (exparg.lastp == savelastp) {
7184 /*
7185 * no matches
7186 */
7187 nometa:
7188 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007189 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007190 exparg.lastp = &str->next;
7191 } else {
7192 *exparg.lastp = NULL;
7193 *savelastp = sp = expsort(*savelastp);
7194 while (sp->next != NULL)
7195 sp = sp->next;
7196 exparg.lastp = &sp->next;
7197 }
7198 str = str->next;
7199 }
7200}
7201
7202/*
7203 * Perform variable substitution and command substitution on an argument,
7204 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7205 * perform splitting and file name expansion. When arglist is NULL, perform
7206 * here document expansion.
7207 */
7208static void
7209expandarg(union node *arg, struct arglist *arglist, int flag)
7210{
7211 struct strlist *sp;
7212 char *p;
7213
7214 argbackq = arg->narg.backquote;
7215 STARTSTACKSTR(expdest);
7216 ifsfirst.next = NULL;
7217 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007218 argstr(arg->narg.text, flag,
7219 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007220 p = _STPUTC('\0', expdest);
7221 expdest = p - 1;
7222 if (arglist == NULL) {
7223 return; /* here document expanded */
7224 }
7225 p = grabstackstr(p);
7226 exparg.lastp = &exparg.list;
7227 /*
7228 * TODO - EXP_REDIR
7229 */
7230 if (flag & EXP_FULL) {
7231 ifsbreakup(p, &exparg);
7232 *exparg.lastp = NULL;
7233 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007234 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007235 } else {
7236 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007237 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007238 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007239 sp->text = p;
7240 *exparg.lastp = sp;
7241 exparg.lastp = &sp->next;
7242 }
7243 if (ifsfirst.next)
7244 ifsfree();
7245 *exparg.lastp = NULL;
7246 if (exparg.list) {
7247 *arglist->lastp = exparg.list;
7248 arglist->lastp = exparg.lastp;
7249 }
7250}
7251
7252/*
7253 * Expand shell variables and backquotes inside a here document.
7254 */
7255static void
7256expandhere(union node *arg, int fd)
7257{
7258 herefd = fd;
7259 expandarg(arg, (struct arglist *)NULL, 0);
7260 full_write(fd, stackblock(), expdest - (char *)stackblock());
7261}
7262
7263/*
7264 * Returns true if the pattern matches the string.
7265 */
7266static int
7267patmatch(char *pattern, const char *string)
7268{
7269 return pmatch(preglob(pattern, 0, 0), string);
7270}
7271
7272/*
7273 * See if a pattern matches in a case statement.
7274 */
7275static int
7276casematch(union node *pattern, char *val)
7277{
7278 struct stackmark smark;
7279 int result;
7280
7281 setstackmark(&smark);
7282 argbackq = pattern->narg.backquote;
7283 STARTSTACKSTR(expdest);
7284 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007285 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7286 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007287 STACKSTRNUL(expdest);
7288 result = patmatch(stackblock(), val);
7289 popstackmark(&smark);
7290 return result;
7291}
7292
7293
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007294/* ============ find_command */
7295
7296struct builtincmd {
7297 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007298 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007299 /* unsigned flags; */
7300};
7301#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007302/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007303 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007305#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007306
7307struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007308 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007309 union param {
7310 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007311 /* index >= 0 for commands without path (slashes) */
7312 /* (TODO: what exactly does the value mean? PATH position?) */
7313 /* index == -1 for commands with slashes */
7314 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007315 const struct builtincmd *cmd;
7316 struct funcnode *func;
7317 } u;
7318};
7319/* values of cmdtype */
7320#define CMDUNKNOWN -1 /* no entry in table for command */
7321#define CMDNORMAL 0 /* command is an executable program */
7322#define CMDFUNCTION 1 /* command is a shell function */
7323#define CMDBUILTIN 2 /* command is a shell builtin */
7324
7325/* action to find_command() */
7326#define DO_ERR 0x01 /* prints errors */
7327#define DO_ABS 0x02 /* checks absolute paths */
7328#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7329#define DO_ALTPATH 0x08 /* using alternate path */
7330#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7331
7332static void find_command(char *, struct cmdentry *, int, const char *);
7333
7334
7335/* ============ Hashing commands */
7336
7337/*
7338 * When commands are first encountered, they are entered in a hash table.
7339 * This ensures that a full path search will not have to be done for them
7340 * on each invocation.
7341 *
7342 * We should investigate converting to a linear search, even though that
7343 * would make the command name "hash" a misnomer.
7344 */
7345
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007346struct tblentry {
7347 struct tblentry *next; /* next entry in hash chain */
7348 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007349 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007351 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007352};
7353
Denis Vlasenko01631112007-12-16 17:20:38 +00007354static struct tblentry **cmdtable;
7355#define INIT_G_cmdtable() do { \
7356 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7357} while (0)
7358
7359static int builtinloc = -1; /* index in path of %builtin, or -1 */
7360
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007361
7362static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007363tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007364{
7365 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007366
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007367#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007368 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007369 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007370 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007371 while (*envp)
7372 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007373 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007374 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007375 /* re-exec ourselves with the new arguments */
7376 execve(bb_busybox_exec_path, argv, envp);
7377 /* If they called chroot or otherwise made the binary no longer
7378 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007379 }
7380#endif
7381
7382 repeat:
7383#ifdef SYSV
7384 do {
7385 execve(cmd, argv, envp);
7386 } while (errno == EINTR);
7387#else
7388 execve(cmd, argv, envp);
7389#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007390 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007392 return;
7393 }
7394 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007395 char **ap;
7396 char **new;
7397
7398 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007399 continue;
7400 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007402 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007403 ap += 2;
7404 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007405 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007406 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007408 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007409 goto repeat;
7410 }
7411}
7412
7413/*
7414 * Exec a program. Never returns. If you change this routine, you may
7415 * have to change the find_command routine as well.
7416 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007417static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418static void
7419shellexec(char **argv, const char *path, int idx)
7420{
7421 char *cmdname;
7422 int e;
7423 char **envp;
7424 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007425#if ENABLE_FEATURE_SH_STANDALONE
7426 int applet_no = -1;
7427#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007428
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007429 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007430 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007431 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007432#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007433 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007434#endif
7435 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007436 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007437 e = errno;
7438 } else {
7439 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007440 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007442 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007443 if (errno != ENOENT && errno != ENOTDIR)
7444 e = errno;
7445 }
7446 stunalloc(cmdname);
7447 }
7448 }
7449
7450 /* Map to POSIX errors */
7451 switch (e) {
7452 case EACCES:
7453 exerrno = 126;
7454 break;
7455 case ENOENT:
7456 exerrno = 127;
7457 break;
7458 default:
7459 exerrno = 2;
7460 break;
7461 }
7462 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007463 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7464 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007465 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7466 /* NOTREACHED */
7467}
7468
7469static void
7470printentry(struct tblentry *cmdp)
7471{
7472 int idx;
7473 const char *path;
7474 char *name;
7475
7476 idx = cmdp->param.index;
7477 path = pathval();
7478 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007479 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007480 stunalloc(name);
7481 } while (--idx >= 0);
7482 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7483}
7484
7485/*
7486 * Clear out command entries. The argument specifies the first entry in
7487 * PATH which has changed.
7488 */
7489static void
7490clearcmdentry(int firstchange)
7491{
7492 struct tblentry **tblp;
7493 struct tblentry **pp;
7494 struct tblentry *cmdp;
7495
7496 INT_OFF;
7497 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7498 pp = tblp;
7499 while ((cmdp = *pp) != NULL) {
7500 if ((cmdp->cmdtype == CMDNORMAL &&
7501 cmdp->param.index >= firstchange)
7502 || (cmdp->cmdtype == CMDBUILTIN &&
7503 builtinloc >= firstchange)
7504 ) {
7505 *pp = cmdp->next;
7506 free(cmdp);
7507 } else {
7508 pp = &cmdp->next;
7509 }
7510 }
7511 }
7512 INT_ON;
7513}
7514
7515/*
7516 * Locate a command in the command hash table. If "add" is nonzero,
7517 * add the command to the table if it is not already present. The
7518 * variable "lastcmdentry" is set to point to the address of the link
7519 * pointing to the entry, so that delete_cmd_entry can delete the
7520 * entry.
7521 *
7522 * Interrupts must be off if called with add != 0.
7523 */
7524static struct tblentry **lastcmdentry;
7525
7526static struct tblentry *
7527cmdlookup(const char *name, int add)
7528{
7529 unsigned int hashval;
7530 const char *p;
7531 struct tblentry *cmdp;
7532 struct tblentry **pp;
7533
7534 p = name;
7535 hashval = (unsigned char)*p << 4;
7536 while (*p)
7537 hashval += (unsigned char)*p++;
7538 hashval &= 0x7FFF;
7539 pp = &cmdtable[hashval % CMDTABLESIZE];
7540 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7541 if (strcmp(cmdp->cmdname, name) == 0)
7542 break;
7543 pp = &cmdp->next;
7544 }
7545 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007546 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7547 + strlen(name)
7548 /* + 1 - already done because
7549 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007550 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007551 cmdp->cmdtype = CMDUNKNOWN;
7552 strcpy(cmdp->cmdname, name);
7553 }
7554 lastcmdentry = pp;
7555 return cmdp;
7556}
7557
7558/*
7559 * Delete the command entry returned on the last lookup.
7560 */
7561static void
7562delete_cmd_entry(void)
7563{
7564 struct tblentry *cmdp;
7565
7566 INT_OFF;
7567 cmdp = *lastcmdentry;
7568 *lastcmdentry = cmdp->next;
7569 if (cmdp->cmdtype == CMDFUNCTION)
7570 freefunc(cmdp->param.func);
7571 free(cmdp);
7572 INT_ON;
7573}
7574
7575/*
7576 * Add a new command entry, replacing any existing command entry for
7577 * the same name - except special builtins.
7578 */
7579static void
7580addcmdentry(char *name, struct cmdentry *entry)
7581{
7582 struct tblentry *cmdp;
7583
7584 cmdp = cmdlookup(name, 1);
7585 if (cmdp->cmdtype == CMDFUNCTION) {
7586 freefunc(cmdp->param.func);
7587 }
7588 cmdp->cmdtype = entry->cmdtype;
7589 cmdp->param = entry->u;
7590 cmdp->rehash = 0;
7591}
7592
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007593static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007594hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007595{
7596 struct tblentry **pp;
7597 struct tblentry *cmdp;
7598 int c;
7599 struct cmdentry entry;
7600 char *name;
7601
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007602 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007603 clearcmdentry(0);
7604 return 0;
7605 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007606
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007607 if (*argptr == NULL) {
7608 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7609 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7610 if (cmdp->cmdtype == CMDNORMAL)
7611 printentry(cmdp);
7612 }
7613 }
7614 return 0;
7615 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007616
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007617 c = 0;
7618 while ((name = *argptr) != NULL) {
7619 cmdp = cmdlookup(name, 0);
7620 if (cmdp != NULL
7621 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007622 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7623 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007624 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007625 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007626 find_command(name, &entry, DO_ERR, pathval());
7627 if (entry.cmdtype == CMDUNKNOWN)
7628 c = 1;
7629 argptr++;
7630 }
7631 return c;
7632}
7633
7634/*
7635 * Called when a cd is done. Marks all commands so the next time they
7636 * are executed they will be rehashed.
7637 */
7638static void
7639hashcd(void)
7640{
7641 struct tblentry **pp;
7642 struct tblentry *cmdp;
7643
7644 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7645 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007646 if (cmdp->cmdtype == CMDNORMAL
7647 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007648 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007649 && builtinloc > 0)
7650 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007651 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007652 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653 }
7654 }
7655}
7656
7657/*
7658 * Fix command hash table when PATH changed.
7659 * Called before PATH is changed. The argument is the new value of PATH;
7660 * pathval() still returns the old value at this point.
7661 * Called with interrupts off.
7662 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007663static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007664changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007665{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007666 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007667 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007668 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007669 int idx_bltin;
7670
7671 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007672 firstchange = 9999; /* assume no change */
7673 idx = 0;
7674 idx_bltin = -1;
7675 for (;;) {
7676 if (*old != *new) {
7677 firstchange = idx;
7678 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007679 || (*old == ':' && *new == '\0')
7680 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007681 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007682 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007683 old = new; /* ignore subsequent differences */
7684 }
7685 if (*new == '\0')
7686 break;
7687 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7688 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007689 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007690 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007691 new++;
7692 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007693 }
7694 if (builtinloc < 0 && idx_bltin >= 0)
7695 builtinloc = idx_bltin; /* zap builtins */
7696 if (builtinloc >= 0 && idx_bltin < 0)
7697 firstchange = 0;
7698 clearcmdentry(firstchange);
7699 builtinloc = idx_bltin;
7700}
7701
7702#define TEOF 0
7703#define TNL 1
7704#define TREDIR 2
7705#define TWORD 3
7706#define TSEMI 4
7707#define TBACKGND 5
7708#define TAND 6
7709#define TOR 7
7710#define TPIPE 8
7711#define TLP 9
7712#define TRP 10
7713#define TENDCASE 11
7714#define TENDBQUOTE 12
7715#define TNOT 13
7716#define TCASE 14
7717#define TDO 15
7718#define TDONE 16
7719#define TELIF 17
7720#define TELSE 18
7721#define TESAC 19
7722#define TFI 20
7723#define TFOR 21
7724#define TIF 22
7725#define TIN 23
7726#define TTHEN 24
7727#define TUNTIL 25
7728#define TWHILE 26
7729#define TBEGIN 27
7730#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007731typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007732
7733/* first char is indicating which tokens mark the end of a list */
7734static const char *const tokname_array[] = {
7735 "\1end of file",
7736 "\0newline",
7737 "\0redirection",
7738 "\0word",
7739 "\0;",
7740 "\0&",
7741 "\0&&",
7742 "\0||",
7743 "\0|",
7744 "\0(",
7745 "\1)",
7746 "\1;;",
7747 "\1`",
7748#define KWDOFFSET 13
7749 /* the following are keywords */
7750 "\0!",
7751 "\0case",
7752 "\1do",
7753 "\1done",
7754 "\1elif",
7755 "\1else",
7756 "\1esac",
7757 "\1fi",
7758 "\0for",
7759 "\0if",
7760 "\0in",
7761 "\1then",
7762 "\0until",
7763 "\0while",
7764 "\0{",
7765 "\1}",
7766};
7767
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007768/* Wrapper around strcmp for qsort/bsearch/... */
7769static int
7770pstrcmp(const void *a, const void *b)
7771{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007772 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007773}
7774
7775static const char *const *
7776findkwd(const char *s)
7777{
7778 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007779 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7780 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007781}
7782
7783/*
7784 * Locate and print what a word is...
7785 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007786static int
7787describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007788{
7789 struct cmdentry entry;
7790 struct tblentry *cmdp;
7791#if ENABLE_ASH_ALIAS
7792 const struct alias *ap;
7793#endif
7794 const char *path = pathval();
7795
7796 if (describe_command_verbose) {
7797 out1str(command);
7798 }
7799
7800 /* First look at the keywords */
7801 if (findkwd(command)) {
7802 out1str(describe_command_verbose ? " is a shell keyword" : command);
7803 goto out;
7804 }
7805
7806#if ENABLE_ASH_ALIAS
7807 /* Then look at the aliases */
7808 ap = lookupalias(command, 0);
7809 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007810 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007811 out1str("alias ");
7812 printalias(ap);
7813 return 0;
7814 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007815 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007816 goto out;
7817 }
7818#endif
7819 /* Then check if it is a tracked alias */
7820 cmdp = cmdlookup(command, 0);
7821 if (cmdp != NULL) {
7822 entry.cmdtype = cmdp->cmdtype;
7823 entry.u = cmdp->param;
7824 } else {
7825 /* Finally use brute force */
7826 find_command(command, &entry, DO_ABS, path);
7827 }
7828
7829 switch (entry.cmdtype) {
7830 case CMDNORMAL: {
7831 int j = entry.u.index;
7832 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007833 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007834 p = command;
7835 } else {
7836 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007837 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007838 stunalloc(p);
7839 } while (--j >= 0);
7840 }
7841 if (describe_command_verbose) {
7842 out1fmt(" is%s %s",
7843 (cmdp ? " a tracked alias for" : nullstr), p
7844 );
7845 } else {
7846 out1str(p);
7847 }
7848 break;
7849 }
7850
7851 case CMDFUNCTION:
7852 if (describe_command_verbose) {
7853 out1str(" is a shell function");
7854 } else {
7855 out1str(command);
7856 }
7857 break;
7858
7859 case CMDBUILTIN:
7860 if (describe_command_verbose) {
7861 out1fmt(" is a %sshell builtin",
7862 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7863 "special " : nullstr
7864 );
7865 } else {
7866 out1str(command);
7867 }
7868 break;
7869
7870 default:
7871 if (describe_command_verbose) {
7872 out1str(": not found\n");
7873 }
7874 return 127;
7875 }
7876 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007877 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007878 return 0;
7879}
7880
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007881static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007882typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007883{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007884 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007885 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007886 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007887
Denis Vlasenko46846e22007-05-20 13:08:31 +00007888 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007889 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007890 i++;
7891 verbose = 0;
7892 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007893 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007894 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007895 }
7896 return err;
7897}
7898
7899#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007900static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007901commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007902{
7903 int c;
7904 enum {
7905 VERIFY_BRIEF = 1,
7906 VERIFY_VERBOSE = 2,
7907 } verify = 0;
7908
7909 while ((c = nextopt("pvV")) != '\0')
7910 if (c == 'V')
7911 verify |= VERIFY_VERBOSE;
7912 else if (c == 'v')
7913 verify |= VERIFY_BRIEF;
7914#if DEBUG
7915 else if (c != 'p')
7916 abort();
7917#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007918 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7919 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007920 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007921 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007922
7923 return 0;
7924}
7925#endif
7926
7927
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007928/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007929
Denis Vlasenko340299a2008-11-21 10:36:36 +00007930static int funcblocksize; /* size of structures in function */
7931static int funcstringsize; /* size of strings in node */
7932static void *funcblock; /* block to allocate function from */
7933static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007934
Eric Andersencb57d552001-06-28 07:25:16 +00007935/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007936#define EV_EXIT 01 /* exit after evaluating tree */
7937#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007938#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007939
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007940static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007941 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7942 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7943 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7944 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7945 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7946 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7947 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7948 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7949 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7950 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7951 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7952 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7953 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7954 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7955 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7956 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7957 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007958#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007959 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007960#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007961 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7962 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7963 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7964 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7965 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7966 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7967 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7968 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7969 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007970};
7971
7972static void calcsize(union node *n);
7973
7974static void
7975sizenodelist(struct nodelist *lp)
7976{
7977 while (lp) {
7978 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7979 calcsize(lp->n);
7980 lp = lp->next;
7981 }
7982}
7983
7984static void
7985calcsize(union node *n)
7986{
7987 if (n == NULL)
7988 return;
7989 funcblocksize += nodesize[n->type];
7990 switch (n->type) {
7991 case NCMD:
7992 calcsize(n->ncmd.redirect);
7993 calcsize(n->ncmd.args);
7994 calcsize(n->ncmd.assign);
7995 break;
7996 case NPIPE:
7997 sizenodelist(n->npipe.cmdlist);
7998 break;
7999 case NREDIR:
8000 case NBACKGND:
8001 case NSUBSHELL:
8002 calcsize(n->nredir.redirect);
8003 calcsize(n->nredir.n);
8004 break;
8005 case NAND:
8006 case NOR:
8007 case NSEMI:
8008 case NWHILE:
8009 case NUNTIL:
8010 calcsize(n->nbinary.ch2);
8011 calcsize(n->nbinary.ch1);
8012 break;
8013 case NIF:
8014 calcsize(n->nif.elsepart);
8015 calcsize(n->nif.ifpart);
8016 calcsize(n->nif.test);
8017 break;
8018 case NFOR:
8019 funcstringsize += strlen(n->nfor.var) + 1;
8020 calcsize(n->nfor.body);
8021 calcsize(n->nfor.args);
8022 break;
8023 case NCASE:
8024 calcsize(n->ncase.cases);
8025 calcsize(n->ncase.expr);
8026 break;
8027 case NCLIST:
8028 calcsize(n->nclist.body);
8029 calcsize(n->nclist.pattern);
8030 calcsize(n->nclist.next);
8031 break;
8032 case NDEFUN:
8033 case NARG:
8034 sizenodelist(n->narg.backquote);
8035 funcstringsize += strlen(n->narg.text) + 1;
8036 calcsize(n->narg.next);
8037 break;
8038 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008039#if ENABLE_ASH_BASH_COMPAT
8040 case NTO2:
8041#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008042 case NCLOBBER:
8043 case NFROM:
8044 case NFROMTO:
8045 case NAPPEND:
8046 calcsize(n->nfile.fname);
8047 calcsize(n->nfile.next);
8048 break;
8049 case NTOFD:
8050 case NFROMFD:
8051 calcsize(n->ndup.vname);
8052 calcsize(n->ndup.next);
8053 break;
8054 case NHERE:
8055 case NXHERE:
8056 calcsize(n->nhere.doc);
8057 calcsize(n->nhere.next);
8058 break;
8059 case NNOT:
8060 calcsize(n->nnot.com);
8061 break;
8062 };
8063}
8064
8065static char *
8066nodeckstrdup(char *s)
8067{
8068 char *rtn = funcstring;
8069
8070 strcpy(funcstring, s);
8071 funcstring += strlen(s) + 1;
8072 return rtn;
8073}
8074
8075static union node *copynode(union node *);
8076
8077static struct nodelist *
8078copynodelist(struct nodelist *lp)
8079{
8080 struct nodelist *start;
8081 struct nodelist **lpp;
8082
8083 lpp = &start;
8084 while (lp) {
8085 *lpp = funcblock;
8086 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8087 (*lpp)->n = copynode(lp->n);
8088 lp = lp->next;
8089 lpp = &(*lpp)->next;
8090 }
8091 *lpp = NULL;
8092 return start;
8093}
8094
8095static union node *
8096copynode(union node *n)
8097{
8098 union node *new;
8099
8100 if (n == NULL)
8101 return NULL;
8102 new = funcblock;
8103 funcblock = (char *) funcblock + nodesize[n->type];
8104
8105 switch (n->type) {
8106 case NCMD:
8107 new->ncmd.redirect = copynode(n->ncmd.redirect);
8108 new->ncmd.args = copynode(n->ncmd.args);
8109 new->ncmd.assign = copynode(n->ncmd.assign);
8110 break;
8111 case NPIPE:
8112 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008113 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008114 break;
8115 case NREDIR:
8116 case NBACKGND:
8117 case NSUBSHELL:
8118 new->nredir.redirect = copynode(n->nredir.redirect);
8119 new->nredir.n = copynode(n->nredir.n);
8120 break;
8121 case NAND:
8122 case NOR:
8123 case NSEMI:
8124 case NWHILE:
8125 case NUNTIL:
8126 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8127 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8128 break;
8129 case NIF:
8130 new->nif.elsepart = copynode(n->nif.elsepart);
8131 new->nif.ifpart = copynode(n->nif.ifpart);
8132 new->nif.test = copynode(n->nif.test);
8133 break;
8134 case NFOR:
8135 new->nfor.var = nodeckstrdup(n->nfor.var);
8136 new->nfor.body = copynode(n->nfor.body);
8137 new->nfor.args = copynode(n->nfor.args);
8138 break;
8139 case NCASE:
8140 new->ncase.cases = copynode(n->ncase.cases);
8141 new->ncase.expr = copynode(n->ncase.expr);
8142 break;
8143 case NCLIST:
8144 new->nclist.body = copynode(n->nclist.body);
8145 new->nclist.pattern = copynode(n->nclist.pattern);
8146 new->nclist.next = copynode(n->nclist.next);
8147 break;
8148 case NDEFUN:
8149 case NARG:
8150 new->narg.backquote = copynodelist(n->narg.backquote);
8151 new->narg.text = nodeckstrdup(n->narg.text);
8152 new->narg.next = copynode(n->narg.next);
8153 break;
8154 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008155#if ENABLE_ASH_BASH_COMPAT
8156 case NTO2:
8157#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008158 case NCLOBBER:
8159 case NFROM:
8160 case NFROMTO:
8161 case NAPPEND:
8162 new->nfile.fname = copynode(n->nfile.fname);
8163 new->nfile.fd = n->nfile.fd;
8164 new->nfile.next = copynode(n->nfile.next);
8165 break;
8166 case NTOFD:
8167 case NFROMFD:
8168 new->ndup.vname = copynode(n->ndup.vname);
8169 new->ndup.dupfd = n->ndup.dupfd;
8170 new->ndup.fd = n->ndup.fd;
8171 new->ndup.next = copynode(n->ndup.next);
8172 break;
8173 case NHERE:
8174 case NXHERE:
8175 new->nhere.doc = copynode(n->nhere.doc);
8176 new->nhere.fd = n->nhere.fd;
8177 new->nhere.next = copynode(n->nhere.next);
8178 break;
8179 case NNOT:
8180 new->nnot.com = copynode(n->nnot.com);
8181 break;
8182 };
8183 new->type = n->type;
8184 return new;
8185}
8186
8187/*
8188 * Make a copy of a parse tree.
8189 */
8190static struct funcnode *
8191copyfunc(union node *n)
8192{
8193 struct funcnode *f;
8194 size_t blocksize;
8195
8196 funcblocksize = offsetof(struct funcnode, n);
8197 funcstringsize = 0;
8198 calcsize(n);
8199 blocksize = funcblocksize;
8200 f = ckmalloc(blocksize + funcstringsize);
8201 funcblock = (char *) f + offsetof(struct funcnode, n);
8202 funcstring = (char *) f + blocksize;
8203 copynode(n);
8204 f->count = 0;
8205 return f;
8206}
8207
8208/*
8209 * Define a shell function.
8210 */
8211static void
8212defun(char *name, union node *func)
8213{
8214 struct cmdentry entry;
8215
8216 INT_OFF;
8217 entry.cmdtype = CMDFUNCTION;
8218 entry.u.func = copyfunc(func);
8219 addcmdentry(name, &entry);
8220 INT_ON;
8221}
8222
Denis Vlasenko4b875702009-03-19 13:30:04 +00008223/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008224#define SKIPBREAK (1 << 0)
8225#define SKIPCONT (1 << 1)
8226#define SKIPFUNC (1 << 2)
8227#define SKIPFILE (1 << 3)
8228#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008229static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008230static int skipcount; /* number of levels to skip */
8231static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008232static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008233
Denis Vlasenko4b875702009-03-19 13:30:04 +00008234/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008235static int evalstring(char *s, int mask);
8236
Denis Vlasenko4b875702009-03-19 13:30:04 +00008237/* Called to execute a trap.
8238 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008239 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008240 *
8241 * Perhaps we should avoid entering new trap handlers
8242 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008243 */
8244static int
8245dotrap(void)
8246{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008247 uint8_t *g;
8248 int sig;
8249 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008250
8251 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008252 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008253 xbarrier();
8254
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008255 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008256 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8257 int want_exexit;
8258 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008259
Denis Vlasenko4b875702009-03-19 13:30:04 +00008260 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008261 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008262 t = trap[sig];
8263 /* non-trapped SIGINT is handled separately by raise_interrupt,
8264 * don't upset it by resetting gotsig[SIGINT-1] */
8265 if (sig == SIGINT && !t)
8266 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008267
8268 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008269 *g = 0;
8270 if (!t)
8271 continue;
8272 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008273 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008274 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008275 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008276 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008277 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008278 }
8279
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008280 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008281 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008282}
8283
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008284/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008285static void evalloop(union node *, int);
8286static void evalfor(union node *, int);
8287static void evalcase(union node *, int);
8288static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008289static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008290static void evalpipe(union node *, int);
8291static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008292static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008293static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008294
Eric Andersen62483552001-07-10 06:09:16 +00008295/*
Eric Andersenc470f442003-07-28 09:56:35 +00008296 * Evaluate a parse tree. The value is left in the global variable
8297 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008298 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008299static void
Eric Andersenc470f442003-07-28 09:56:35 +00008300evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008301{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008302 struct jmploc *volatile savehandler = exception_handler;
8303 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008304 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008305 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008306 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008307 int int_level;
8308
8309 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008310
Eric Andersenc470f442003-07-28 09:56:35 +00008311 if (n == NULL) {
8312 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008313 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008314 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008315 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008316
8317 exception_handler = &jmploc;
8318 {
8319 int err = setjmp(jmploc.loc);
8320 if (err) {
8321 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008322 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008323 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8324 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008325 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008326 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008327 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008328 TRACE(("exception %d in evaltree, propagating err=%d\n",
8329 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008330 exception_handler = savehandler;
8331 longjmp(exception_handler->loc, err);
8332 }
8333 }
8334
Eric Andersenc470f442003-07-28 09:56:35 +00008335 switch (n->type) {
8336 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008337#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008338 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008339 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008340 break;
8341#endif
8342 case NNOT:
8343 evaltree(n->nnot.com, EV_TESTED);
8344 status = !exitstatus;
8345 goto setstatus;
8346 case NREDIR:
8347 expredir(n->nredir.redirect);
8348 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8349 if (!status) {
8350 evaltree(n->nredir.n, flags & EV_TESTED);
8351 status = exitstatus;
8352 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008353 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008354 goto setstatus;
8355 case NCMD:
8356 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008357 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008358 if (eflag && !(flags & EV_TESTED))
8359 checkexit = ~0;
8360 goto calleval;
8361 case NFOR:
8362 evalfn = evalfor;
8363 goto calleval;
8364 case NWHILE:
8365 case NUNTIL:
8366 evalfn = evalloop;
8367 goto calleval;
8368 case NSUBSHELL:
8369 case NBACKGND:
8370 evalfn = evalsubshell;
8371 goto calleval;
8372 case NPIPE:
8373 evalfn = evalpipe;
8374 goto checkexit;
8375 case NCASE:
8376 evalfn = evalcase;
8377 goto calleval;
8378 case NAND:
8379 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008380 case NSEMI: {
8381
Eric Andersenc470f442003-07-28 09:56:35 +00008382#if NAND + 1 != NOR
8383#error NAND + 1 != NOR
8384#endif
8385#if NOR + 1 != NSEMI
8386#error NOR + 1 != NSEMI
8387#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008388 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008389 evaltree(
8390 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008391 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008392 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008393 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008394 break;
8395 if (!evalskip) {
8396 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008397 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008398 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008399 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008400 evalfn(n, flags);
8401 break;
8402 }
8403 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008404 }
Eric Andersenc470f442003-07-28 09:56:35 +00008405 case NIF:
8406 evaltree(n->nif.test, EV_TESTED);
8407 if (evalskip)
8408 break;
8409 if (exitstatus == 0) {
8410 n = n->nif.ifpart;
8411 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008412 }
8413 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008414 n = n->nif.elsepart;
8415 goto evaln;
8416 }
8417 goto success;
8418 case NDEFUN:
8419 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008420 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008421 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008422 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008423 exitstatus = status;
8424 break;
8425 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008426
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008427 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008428 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008429
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008430 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008431 /* Order of checks below is important:
8432 * signal handlers trigger before exit caused by "set -e".
8433 */
8434 if (pending_sig && dotrap())
8435 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008436 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008437 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008438
8439 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008440 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008441 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008442 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008443
8444 RESTORE_INT(int_level);
8445 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008446}
8447
Eric Andersenc470f442003-07-28 09:56:35 +00008448#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8449static
8450#endif
8451void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8452
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008453static void
Eric Andersenc470f442003-07-28 09:56:35 +00008454evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008455{
8456 int status;
8457
8458 loopnest++;
8459 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008460 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008461 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008462 int i;
8463
Eric Andersencb57d552001-06-28 07:25:16 +00008464 evaltree(n->nbinary.ch1, EV_TESTED);
8465 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008466 skipping:
8467 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008468 evalskip = 0;
8469 continue;
8470 }
8471 if (evalskip == SKIPBREAK && --skipcount <= 0)
8472 evalskip = 0;
8473 break;
8474 }
Eric Andersenc470f442003-07-28 09:56:35 +00008475 i = exitstatus;
8476 if (n->type != NWHILE)
8477 i = !i;
8478 if (i != 0)
8479 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008480 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008481 status = exitstatus;
8482 if (evalskip)
8483 goto skipping;
8484 }
8485 loopnest--;
8486 exitstatus = status;
8487}
8488
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008489static void
Eric Andersenc470f442003-07-28 09:56:35 +00008490evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008491{
8492 struct arglist arglist;
8493 union node *argp;
8494 struct strlist *sp;
8495 struct stackmark smark;
8496
8497 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008498 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008499 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008500 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008501 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008502 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008503 if (evalskip)
8504 goto out;
8505 }
8506 *arglist.lastp = NULL;
8507
8508 exitstatus = 0;
8509 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008510 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008511 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008512 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008513 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008514 if (evalskip) {
8515 if (evalskip == SKIPCONT && --skipcount <= 0) {
8516 evalskip = 0;
8517 continue;
8518 }
8519 if (evalskip == SKIPBREAK && --skipcount <= 0)
8520 evalskip = 0;
8521 break;
8522 }
8523 }
8524 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008525 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008526 popstackmark(&smark);
8527}
8528
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008529static void
Eric Andersenc470f442003-07-28 09:56:35 +00008530evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008531{
8532 union node *cp;
8533 union node *patp;
8534 struct arglist arglist;
8535 struct stackmark smark;
8536
8537 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008538 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008539 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008540 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008541 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008542 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8543 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008544 if (casematch(patp, arglist.list->text)) {
8545 if (evalskip == 0) {
8546 evaltree(cp->nclist.body, flags);
8547 }
8548 goto out;
8549 }
8550 }
8551 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008552 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008553 popstackmark(&smark);
8554}
8555
Eric Andersenc470f442003-07-28 09:56:35 +00008556/*
8557 * Kick off a subshell to evaluate a tree.
8558 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008559static void
Eric Andersenc470f442003-07-28 09:56:35 +00008560evalsubshell(union node *n, int flags)
8561{
8562 struct job *jp;
8563 int backgnd = (n->type == NBACKGND);
8564 int status;
8565
8566 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008567 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008568 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008569 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008570 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008571 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008572 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008573 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008574 flags |= EV_EXIT;
8575 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008576 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008577 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008578 redirect(n->nredir.redirect, 0);
8579 evaltreenr(n->nredir.n, flags);
8580 /* never returns */
8581 }
8582 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008583 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008584 status = waitforjob(jp);
8585 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008586 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008587}
8588
Eric Andersenc470f442003-07-28 09:56:35 +00008589/*
8590 * Compute the names of the files in a redirection list.
8591 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008592static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008593static void
8594expredir(union node *n)
8595{
8596 union node *redir;
8597
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008598 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008599 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008600
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008601 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008602 fn.lastp = &fn.list;
8603 switch (redir->type) {
8604 case NFROMTO:
8605 case NFROM:
8606 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008607#if ENABLE_ASH_BASH_COMPAT
8608 case NTO2:
8609#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008610 case NCLOBBER:
8611 case NAPPEND:
8612 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008613#if ENABLE_ASH_BASH_COMPAT
8614 store_expfname:
8615#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008616 redir->nfile.expfname = fn.list->text;
8617 break;
8618 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008619 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008620 if (redir->ndup.vname) {
8621 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008622 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008623 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008624#if ENABLE_ASH_BASH_COMPAT
8625//FIXME: we used expandarg with different args!
8626 if (!isdigit_str9(fn.list->text)) {
8627 /* >&file, not >&fd */
8628 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8629 ash_msg_and_raise_error("redir error");
8630 redir->type = NTO2;
8631 goto store_expfname;
8632 }
8633#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008634 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008635 }
8636 break;
8637 }
8638 }
8639}
8640
Eric Andersencb57d552001-06-28 07:25:16 +00008641/*
Eric Andersencb57d552001-06-28 07:25:16 +00008642 * Evaluate a pipeline. All the processes in the pipeline are children
8643 * of the process creating the pipeline. (This differs from some versions
8644 * of the shell, which make the last process in a pipeline the parent
8645 * of all the rest.)
8646 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008647static void
Eric Andersenc470f442003-07-28 09:56:35 +00008648evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008649{
8650 struct job *jp;
8651 struct nodelist *lp;
8652 int pipelen;
8653 int prevfd;
8654 int pip[2];
8655
Eric Andersenc470f442003-07-28 09:56:35 +00008656 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008657 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008658 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008659 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008660 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008661 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008662 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008663 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008664 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008665 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008666 pip[1] = -1;
8667 if (lp->next) {
8668 if (pipe(pip) < 0) {
8669 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008670 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008671 }
8672 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008673 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008674 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008675 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008676 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008677 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008678 if (prevfd > 0) {
8679 dup2(prevfd, 0);
8680 close(prevfd);
8681 }
8682 if (pip[1] > 1) {
8683 dup2(pip[1], 1);
8684 close(pip[1]);
8685 }
Eric Andersenc470f442003-07-28 09:56:35 +00008686 evaltreenr(lp->n, flags);
8687 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008688 }
8689 if (prevfd >= 0)
8690 close(prevfd);
8691 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008692 /* Don't want to trigger debugging */
8693 if (pip[1] != -1)
8694 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008695 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008696 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008697 exitstatus = waitforjob(jp);
8698 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008699 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008700 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008701}
8702
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008703/*
8704 * Controls whether the shell is interactive or not.
8705 */
8706static void
8707setinteractive(int on)
8708{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008709 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008710
8711 if (++on == is_interactive)
8712 return;
8713 is_interactive = on;
8714 setsignal(SIGINT);
8715 setsignal(SIGQUIT);
8716 setsignal(SIGTERM);
8717#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8718 if (is_interactive > 1) {
8719 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008720 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008721
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008722 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008723 /* note: ash and hush share this string */
8724 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008725 "Enter 'help' for a list of built-in commands."
8726 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008727 bb_banner,
8728 "built-in shell (ash)"
8729 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008730 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008731 }
8732 }
8733#endif
8734}
8735
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008736static void
8737optschanged(void)
8738{
8739#if DEBUG
8740 opentrace();
8741#endif
8742 setinteractive(iflag);
8743 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008744#if ENABLE_FEATURE_EDITING_VI
8745 if (viflag)
8746 line_input_state->flags |= VI_MODE;
8747 else
8748 line_input_state->flags &= ~VI_MODE;
8749#else
8750 viflag = 0; /* forcibly keep the option off */
8751#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008752}
8753
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008754static struct localvar *localvars;
8755
8756/*
8757 * Called after a function returns.
8758 * Interrupts must be off.
8759 */
8760static void
8761poplocalvars(void)
8762{
8763 struct localvar *lvp;
8764 struct var *vp;
8765
8766 while ((lvp = localvars) != NULL) {
8767 localvars = lvp->next;
8768 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008769 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 if (vp == NULL) { /* $- saved */
8771 memcpy(optlist, lvp->text, sizeof(optlist));
8772 free((char*)lvp->text);
8773 optschanged();
8774 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008775 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008776 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008777 if (vp->var_func)
8778 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008779 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008780 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008781 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008782 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008783 }
8784 free(lvp);
8785 }
8786}
8787
8788static int
8789evalfun(struct funcnode *func, int argc, char **argv, int flags)
8790{
8791 volatile struct shparam saveparam;
8792 struct localvar *volatile savelocalvars;
8793 struct jmploc *volatile savehandler;
8794 struct jmploc jmploc;
8795 int e;
8796
8797 saveparam = shellparam;
8798 savelocalvars = localvars;
8799 e = setjmp(jmploc.loc);
8800 if (e) {
8801 goto funcdone;
8802 }
8803 INT_OFF;
8804 savehandler = exception_handler;
8805 exception_handler = &jmploc;
8806 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008807 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008808 func->count++;
8809 funcnest++;
8810 INT_ON;
8811 shellparam.nparam = argc - 1;
8812 shellparam.p = argv + 1;
8813#if ENABLE_ASH_GETOPTS
8814 shellparam.optind = 1;
8815 shellparam.optoff = -1;
8816#endif
8817 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008818 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 INT_OFF;
8820 funcnest--;
8821 freefunc(func);
8822 poplocalvars();
8823 localvars = savelocalvars;
8824 freeparam(&shellparam);
8825 shellparam = saveparam;
8826 exception_handler = savehandler;
8827 INT_ON;
8828 evalskip &= ~SKIPFUNC;
8829 return e;
8830}
8831
Denis Vlasenko131ae172007-02-18 13:00:19 +00008832#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008833static char **
8834parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008835{
8836 char *cp, c;
8837
8838 for (;;) {
8839 cp = *++argv;
8840 if (!cp)
8841 return 0;
8842 if (*cp++ != '-')
8843 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008844 c = *cp++;
8845 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008846 break;
8847 if (c == '-' && !*cp) {
8848 argv++;
8849 break;
8850 }
8851 do {
8852 switch (c) {
8853 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008854 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008855 break;
8856 default:
8857 /* run 'typecmd' for other options */
8858 return 0;
8859 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008860 c = *cp++;
8861 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008862 }
8863 return argv;
8864}
8865#endif
8866
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008867/*
8868 * Make a variable a local variable. When a variable is made local, it's
8869 * value and flags are saved in a localvar structure. The saved values
8870 * will be restored when the shell function returns. We handle the name
8871 * "-" as a special case.
8872 */
8873static void
8874mklocal(char *name)
8875{
8876 struct localvar *lvp;
8877 struct var **vpp;
8878 struct var *vp;
8879
8880 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008881 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008882 if (LONE_DASH(name)) {
8883 char *p;
8884 p = ckmalloc(sizeof(optlist));
8885 lvp->text = memcpy(p, optlist, sizeof(optlist));
8886 vp = NULL;
8887 } else {
8888 char *eq;
8889
8890 vpp = hashvar(name);
8891 vp = *findvar(vpp, name);
8892 eq = strchr(name, '=');
8893 if (vp == NULL) {
8894 if (eq)
8895 setvareq(name, VSTRFIXED);
8896 else
8897 setvar(name, NULL, VSTRFIXED);
8898 vp = *vpp; /* the new variable */
8899 lvp->flags = VUNSET;
8900 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008901 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008902 lvp->flags = vp->flags;
8903 vp->flags |= VSTRFIXED|VTEXTFIXED;
8904 if (eq)
8905 setvareq(name, 0);
8906 }
8907 }
8908 lvp->vp = vp;
8909 lvp->next = localvars;
8910 localvars = lvp;
8911 INT_ON;
8912}
8913
8914/*
8915 * The "local" command.
8916 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008917static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008918localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008919{
8920 char *name;
8921
8922 argv = argptr;
8923 while ((name = *argv++) != NULL) {
8924 mklocal(name);
8925 }
8926 return 0;
8927}
8928
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008929static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008930falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008931{
8932 return 1;
8933}
8934
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008935static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008936truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008937{
8938 return 0;
8939}
8940
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008941static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008942execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008943{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008944 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008945 iflag = 0; /* exit on error */
8946 mflag = 0;
8947 optschanged();
8948 shellexec(argv + 1, pathval(), 0);
8949 }
8950 return 0;
8951}
8952
8953/*
8954 * The return command.
8955 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008956static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008957returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008958{
8959 /*
8960 * If called outside a function, do what ksh does;
8961 * skip the rest of the file.
8962 */
8963 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8964 return argv[1] ? number(argv[1]) : exitstatus;
8965}
8966
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008967/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008968static int breakcmd(int, char **) FAST_FUNC;
8969static int dotcmd(int, char **) FAST_FUNC;
8970static int evalcmd(int, char **) FAST_FUNC;
8971static int exitcmd(int, char **) FAST_FUNC;
8972static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008973#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008974static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008975#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008976#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008977static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008978#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008979#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008980static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008981#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008982static int readcmd(int, char **) FAST_FUNC;
8983static int setcmd(int, char **) FAST_FUNC;
8984static int shiftcmd(int, char **) FAST_FUNC;
8985static int timescmd(int, char **) FAST_FUNC;
8986static int trapcmd(int, char **) FAST_FUNC;
8987static int umaskcmd(int, char **) FAST_FUNC;
8988static int unsetcmd(int, char **) FAST_FUNC;
8989static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008990
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008991#define BUILTIN_NOSPEC "0"
8992#define BUILTIN_SPECIAL "1"
8993#define BUILTIN_REGULAR "2"
8994#define BUILTIN_SPEC_REG "3"
8995#define BUILTIN_ASSIGN "4"
8996#define BUILTIN_SPEC_ASSG "5"
8997#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008998#define BUILTIN_SPEC_REG_ASSG "7"
8999
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009000/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009001#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009002static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009003#endif
9004#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009005static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009006#endif
9007#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009008static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009009#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009010
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009011/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009012static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009013 { BUILTIN_SPEC_REG "." , dotcmd },
9014 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009015#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009016 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009017#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009018 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009019#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009020#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009021#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009022 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009023#endif
9024#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009025 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009026#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009027 { BUILTIN_SPEC_REG "break" , breakcmd },
9028 { BUILTIN_REGULAR "cd" , cdcmd },
9029 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009030#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009031 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009032#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009033 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009034#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009035 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009037 { BUILTIN_SPEC_REG "eval" , evalcmd },
9038 { BUILTIN_SPEC_REG "exec" , execcmd },
9039 { BUILTIN_SPEC_REG "exit" , exitcmd },
9040 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9041 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009042#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009043 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009044#endif
9045#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009046 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009047#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009048 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009049#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009050 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009051#endif
9052#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009053 { BUILTIN_REGULAR "jobs" , jobscmd },
9054 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009056#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009057 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009058#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009059 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009060#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009062#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009063 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9064 { BUILTIN_REGULAR "read" , readcmd },
9065 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9066 { BUILTIN_SPEC_REG "return" , returncmd },
9067 { BUILTIN_SPEC_REG "set" , setcmd },
9068 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009069#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009070 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009071#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009074#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009075 { BUILTIN_SPEC_REG "times" , timescmd },
9076 { BUILTIN_SPEC_REG "trap" , trapcmd },
9077 { BUILTIN_REGULAR "true" , truecmd },
9078 { BUILTIN_NOSPEC "type" , typecmd },
9079 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9080 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009081#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009083#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9085 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009086};
9087
Denis Vlasenko80591b02008-03-25 07:49:43 +00009088/* Should match the above table! */
9089#define COMMANDCMD (builtintab + \
9090 2 + \
9091 1 * ENABLE_ASH_BUILTIN_TEST + \
9092 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9093 1 * ENABLE_ASH_ALIAS + \
9094 1 * ENABLE_ASH_JOB_CONTROL + \
9095 3)
9096#define EXECCMD (builtintab + \
9097 2 + \
9098 1 * ENABLE_ASH_BUILTIN_TEST + \
9099 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9100 1 * ENABLE_ASH_ALIAS + \
9101 1 * ENABLE_ASH_JOB_CONTROL + \
9102 3 + \
9103 1 * ENABLE_ASH_CMDCMD + \
9104 1 + \
9105 ENABLE_ASH_BUILTIN_ECHO + \
9106 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009107
9108/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009109 * Search the table of builtin commands.
9110 */
9111static struct builtincmd *
9112find_builtin(const char *name)
9113{
9114 struct builtincmd *bp;
9115
9116 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009117 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009118 pstrcmp
9119 );
9120 return bp;
9121}
9122
9123/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009124 * Execute a simple command.
9125 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009126static int
9127isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009128{
9129 const char *q = endofname(p);
9130 if (p == q)
9131 return 0;
9132 return *q == '=';
9133}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009134static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009135bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009136{
9137 /* Preserve exitstatus of a previous possible redirection
9138 * as POSIX mandates */
9139 return back_exitstatus;
9140}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009141static void
Eric Andersenc470f442003-07-28 09:56:35 +00009142evalcommand(union node *cmd, int flags)
9143{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009144 static const struct builtincmd null_bltin = {
9145 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009146 };
Eric Andersenc470f442003-07-28 09:56:35 +00009147 struct stackmark smark;
9148 union node *argp;
9149 struct arglist arglist;
9150 struct arglist varlist;
9151 char **argv;
9152 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009153 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009154 struct cmdentry cmdentry;
9155 struct job *jp;
9156 char *lastarg;
9157 const char *path;
9158 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009159 int status;
9160 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009161 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009162 smallint cmd_is_exec;
9163 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009164
9165 /* First expand the arguments. */
9166 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9167 setstackmark(&smark);
9168 back_exitstatus = 0;
9169
9170 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009171 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009172 varlist.lastp = &varlist.list;
9173 *varlist.lastp = NULL;
9174 arglist.lastp = &arglist.list;
9175 *arglist.lastp = NULL;
9176
9177 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009178 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009179 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9180 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9181 }
9182
Eric Andersenc470f442003-07-28 09:56:35 +00009183 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9184 struct strlist **spp;
9185
9186 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009187 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009188 expandarg(argp, &arglist, EXP_VARTILDE);
9189 else
9190 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9191
Eric Andersenc470f442003-07-28 09:56:35 +00009192 for (sp = *spp; sp; sp = sp->next)
9193 argc++;
9194 }
9195
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009196 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009197 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009198 TRACE(("evalcommand arg: %s\n", sp->text));
9199 *nargv++ = sp->text;
9200 }
9201 *nargv = NULL;
9202
9203 lastarg = NULL;
9204 if (iflag && funcnest == 0 && argc > 0)
9205 lastarg = nargv[-1];
9206
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009207 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009208 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009209 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009210
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009211 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009212 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9213 struct strlist **spp;
9214 char *p;
9215
9216 spp = varlist.lastp;
9217 expandarg(argp, &varlist, EXP_VARTILDE);
9218
9219 /*
9220 * Modify the command lookup path, if a PATH= assignment
9221 * is present
9222 */
9223 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009224 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009225 path = p;
9226 }
9227
9228 /* Print the command if xflag is set. */
9229 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009230 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009231 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009232
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009233 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009234 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009235 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009236 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009237 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009238 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009239 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009240 }
9241 sp = arglist.list;
9242 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009243 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009244 }
9245
9246 cmd_is_exec = 0;
9247 spclbltin = -1;
9248
9249 /* Now locate the command. */
9250 if (argc) {
9251 const char *oldpath;
9252 int cmd_flag = DO_ERR;
9253
9254 path += 5;
9255 oldpath = path;
9256 for (;;) {
9257 find_command(argv[0], &cmdentry, cmd_flag, path);
9258 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009259 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009260 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009261 goto bail;
9262 }
9263
9264 /* implement bltin and command here */
9265 if (cmdentry.cmdtype != CMDBUILTIN)
9266 break;
9267 if (spclbltin < 0)
9268 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9269 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009270 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009271#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009272 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009273 path = oldpath;
9274 nargv = parse_command_args(argv, &path);
9275 if (!nargv)
9276 break;
9277 argc -= nargv - argv;
9278 argv = nargv;
9279 cmd_flag |= DO_NOFUNC;
9280 } else
9281#endif
9282 break;
9283 }
9284 }
9285
9286 if (status) {
9287 /* We have a redirection error. */
9288 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009289 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009290 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009291 exitstatus = status;
9292 goto out;
9293 }
9294
9295 /* Execute the command. */
9296 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009297 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009298
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009299#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009300/* (1) BUG: if variables are set, we need to fork, or save/restore them
9301 * around run_nofork_applet() call.
9302 * (2) Should this check also be done in forkshell()?
9303 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9304 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009305 /* find_command() encodes applet_no as (-2 - applet_no) */
9306 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009307 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009308 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009309 /* run <applet>_main() */
9310 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009311 break;
9312 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009313#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009314 /* Can we avoid forking off? For example, very last command
9315 * in a script or a subshell does not need forking,
9316 * we can just exec it.
9317 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009318 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009319 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009320 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009321 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009322 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009323 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009324 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009325 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009326 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009327 break;
9328 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009329 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009330 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009331 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009332 }
9333 listsetvar(varlist.list, VEXPORT|VSTACK);
9334 shellexec(argv, path, cmdentry.u.index);
9335 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009336 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009337 case CMDBUILTIN:
9338 cmdenviron = varlist.list;
9339 if (cmdenviron) {
9340 struct strlist *list = cmdenviron;
9341 int i = VNOSET;
9342 if (spclbltin > 0 || argc == 0) {
9343 i = 0;
9344 if (cmd_is_exec && argc > 1)
9345 i = VEXPORT;
9346 }
9347 listsetvar(list, i);
9348 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009349 /* Tight loop with builtins only:
9350 * "while kill -0 $child; do true; done"
9351 * will never exit even if $child died, unless we do this
9352 * to reap the zombie and make kill detect that it's gone: */
9353 dowait(DOWAIT_NONBLOCK, NULL);
9354
Eric Andersenc470f442003-07-28 09:56:35 +00009355 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9356 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009357 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009358 if (i == EXEXIT)
9359 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009360 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009361 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009362 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009363 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009364 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009365 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009366 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009367 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009368 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009369 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009370 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009371 }
9372 break;
9373
9374 case CMDFUNCTION:
9375 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009376 /* See above for the rationale */
9377 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009378 if (evalfun(cmdentry.u.func, argc, argv, flags))
9379 goto raise;
9380 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009381
9382 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009383
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009384 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009385 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009386 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009387 /* dsl: I think this is intended to be used to support
9388 * '_' in 'vi' command mode during line editing...
9389 * However I implemented that within libedit itself.
9390 */
9391 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009392 }
Eric Andersenc470f442003-07-28 09:56:35 +00009393 popstackmark(&smark);
9394}
9395
9396static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009397evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9398{
Eric Andersenc470f442003-07-28 09:56:35 +00009399 char *volatile savecmdname;
9400 struct jmploc *volatile savehandler;
9401 struct jmploc jmploc;
9402 int i;
9403
9404 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009405 i = setjmp(jmploc.loc);
9406 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009407 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009408 savehandler = exception_handler;
9409 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009410 commandname = argv[0];
9411 argptr = argv + 1;
9412 optptr = NULL; /* initialize nextopt */
9413 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009414 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009415 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009416 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009417 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009418 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009419 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009420
9421 return i;
9422}
9423
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009424static int
9425goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009426{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009427 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009428}
9429
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009430
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009431/*
9432 * Search for a command. This is called before we fork so that the
9433 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009434 * the child. The check for "goodname" is an overly conservative
9435 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009436 */
Eric Andersenc470f442003-07-28 09:56:35 +00009437static void
9438prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009439{
9440 struct cmdentry entry;
9441
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009442 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9443 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009444}
9445
Eric Andersencb57d552001-06-28 07:25:16 +00009446
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009447/* ============ Builtin commands
9448 *
9449 * Builtin commands whose functions are closely tied to evaluation
9450 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009451 */
9452
9453/*
Eric Andersencb57d552001-06-28 07:25:16 +00009454 * Handle break and continue commands. Break, continue, and return are
9455 * all handled by setting the evalskip flag. The evaluation routines
9456 * above all check this flag, and if it is set they start skipping
9457 * commands rather than executing them. The variable skipcount is
9458 * the number of loops to break/continue, or the number of function
9459 * levels to return. (The latter is always 1.) It should probably
9460 * be an error to break out of more loops than exist, but it isn't
9461 * in the standard shell so we don't make it one here.
9462 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009463static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009464breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009465{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009466 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009467
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009468 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009469 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009470 if (n > loopnest)
9471 n = loopnest;
9472 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009473 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009474 skipcount = n;
9475 }
9476 return 0;
9477}
9478
Eric Andersenc470f442003-07-28 09:56:35 +00009479
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009480/* ============ input.c
9481 *
Eric Andersen90898442003-08-06 11:20:52 +00009482 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009483 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009484
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009485enum {
9486 INPUT_PUSH_FILE = 1,
9487 INPUT_NOFILE_OK = 2,
9488};
Eric Andersencb57d552001-06-28 07:25:16 +00009489
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009490static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009491/* values of checkkwd variable */
9492#define CHKALIAS 0x1
9493#define CHKKWD 0x2
9494#define CHKNL 0x4
9495
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009496/*
9497 * Push a string back onto the input at this current parsefile level.
9498 * We handle aliases this way.
9499 */
9500#if !ENABLE_ASH_ALIAS
9501#define pushstring(s, ap) pushstring(s)
9502#endif
9503static void
9504pushstring(char *s, struct alias *ap)
9505{
9506 struct strpush *sp;
9507 int len;
9508
9509 len = strlen(s);
9510 INT_OFF;
9511 if (g_parsefile->strpush) {
9512 sp = ckzalloc(sizeof(*sp));
9513 sp->prev = g_parsefile->strpush;
9514 } else {
9515 sp = &(g_parsefile->basestrpush);
9516 }
9517 g_parsefile->strpush = sp;
9518 sp->prev_string = g_parsefile->next_to_pgetc;
9519 sp->prev_left_in_line = g_parsefile->left_in_line;
9520#if ENABLE_ASH_ALIAS
9521 sp->ap = ap;
9522 if (ap) {
9523 ap->flag |= ALIASINUSE;
9524 sp->string = s;
9525 }
9526#endif
9527 g_parsefile->next_to_pgetc = s;
9528 g_parsefile->left_in_line = len;
9529 INT_ON;
9530}
9531
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009532static void
9533popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009534{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009535 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009536
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009537 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009538#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009539 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009540 if (g_parsefile->next_to_pgetc[-1] == ' '
9541 || g_parsefile->next_to_pgetc[-1] == '\t'
9542 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009543 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009544 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009545 if (sp->string != sp->ap->val) {
9546 free(sp->string);
9547 }
9548 sp->ap->flag &= ~ALIASINUSE;
9549 if (sp->ap->flag & ALIASDEAD) {
9550 unalias(sp->ap->name);
9551 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009552 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009553#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009554 g_parsefile->next_to_pgetc = sp->prev_string;
9555 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009556 g_parsefile->strpush = sp->prev;
9557 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009558 free(sp);
9559 INT_ON;
9560}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009561
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009562//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9563//it peeks whether it is &>, and then pushes back both chars.
9564//This function needs to save last *next_to_pgetc to buf[0]
9565//to make two pungetc() reliable. Currently,
9566// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009567static int
9568preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009569{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009570 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009571 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009572
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009573 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009574#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009575 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009576 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9577 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009578 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009579 int timeout = -1;
9580# if ENABLE_ASH_IDLE_TIMEOUT
9581 if (iflag) {
9582 const char *tmout_var = lookupvar("TMOUT");
9583 if (tmout_var) {
9584 timeout = atoi(tmout_var) * 1000;
9585 if (timeout <= 0)
9586 timeout = -1;
9587 }
9588 }
9589# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009590# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009591 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009592# endif
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009593 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009594 if (nr == 0) {
9595 /* Ctrl+C pressed */
9596 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009597 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009598 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009599 raise(SIGINT);
9600 return 1;
9601 }
Eric Andersenc470f442003-07-28 09:56:35 +00009602 goto retry;
9603 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009604 if (nr < 0) {
9605 if (errno == 0) {
9606 /* Ctrl+D pressed */
9607 nr = 0;
9608 }
9609# if ENABLE_ASH_IDLE_TIMEOUT
9610 else if (errno == EAGAIN && timeout > 0) {
9611 printf("\007timed out waiting for input: auto-logout\n");
9612 exitshell();
9613 }
9614# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009615 }
Eric Andersencb57d552001-06-28 07:25:16 +00009616 }
9617#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009618 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009619#endif
9620
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009621#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009622 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009623 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009624 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009625 if (flags >= 0 && (flags & O_NONBLOCK)) {
9626 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009627 if (fcntl(0, F_SETFL, flags) >= 0) {
9628 out2str("sh: turning off NDELAY mode\n");
9629 goto retry;
9630 }
9631 }
9632 }
9633 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009634#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009635 return nr;
9636}
9637
9638/*
9639 * Refill the input buffer and return the next input character:
9640 *
9641 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009642 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9643 * or we are reading from a string so we can't refill the buffer,
9644 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009645 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009646 * 4) Process input up to the next newline, deleting nul characters.
9647 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009648//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9649#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009650static int
Eric Andersenc470f442003-07-28 09:56:35 +00009651preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009652{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009653 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009654 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009655
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009656 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009657#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009658 if (g_parsefile->left_in_line == -1
9659 && g_parsefile->strpush->ap
9660 && g_parsefile->next_to_pgetc[-1] != ' '
9661 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009662 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009663 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009664 return PEOA;
9665 }
Eric Andersen2870d962001-07-02 17:27:21 +00009666#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009667 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009668 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009669 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9670 g_parsefile->left_in_line,
9671 g_parsefile->next_to_pgetc,
9672 g_parsefile->next_to_pgetc);
9673 if (--g_parsefile->left_in_line >= 0)
9674 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009675 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009676 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009677 * "pgetc" needs refilling.
9678 */
9679
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009680 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009681 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009682 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009683 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009684 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009685 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009686 /* even in failure keep left_in_line and next_to_pgetc
9687 * in lock step, for correct multi-layer pungetc.
9688 * left_in_line was decremented before preadbuffer(),
9689 * must inc next_to_pgetc: */
9690 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009691 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009692 }
Eric Andersencb57d552001-06-28 07:25:16 +00009693
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009694 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009695 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009696 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009697 again:
9698 more = preadfd();
9699 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009700 /* don't try reading again */
9701 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009702 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009703 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009704 return PEOF;
9705 }
9706 }
9707
Denis Vlasenko727752d2008-11-28 03:41:47 +00009708 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009709 * Set g_parsefile->left_in_line
9710 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009711 * NUL chars are deleted.
9712 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009713 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009714 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009715 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009716
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009717 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009718
Denis Vlasenko727752d2008-11-28 03:41:47 +00009719 c = *q;
9720 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009721 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009722 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009723 q++;
9724 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009725 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009726 break;
9727 }
Eric Andersencb57d552001-06-28 07:25:16 +00009728 }
9729
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009730 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009731 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9732 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009733 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009734 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009735 }
9736 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009737 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009738
Eric Andersencb57d552001-06-28 07:25:16 +00009739 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009740 char save = *q;
9741 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009742 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009743 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009744 }
9745
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 pgetc_debug("preadbuffer at %d:%p'%s'",
9747 g_parsefile->left_in_line,
9748 g_parsefile->next_to_pgetc,
9749 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009750 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009751}
9752
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009753#define pgetc_as_macro() \
9754 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009755 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009756 : preadbuffer() \
9757 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009759static int
9760pgetc(void)
9761{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009762 pgetc_debug("pgetc_fast at %d:%p'%s'",
9763 g_parsefile->left_in_line,
9764 g_parsefile->next_to_pgetc,
9765 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009766 return pgetc_as_macro();
9767}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009768
9769#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009770# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009771#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009772# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009773#endif
9774
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009775#if ENABLE_ASH_ALIAS
9776static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009777pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009778{
9779 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009780 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009781 pgetc_debug("pgetc_fast at %d:%p'%s'",
9782 g_parsefile->left_in_line,
9783 g_parsefile->next_to_pgetc,
9784 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009785 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009786 } while (c == PEOA);
9787 return c;
9788}
9789#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009790# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009791#endif
9792
9793/*
9794 * Read a line from the script.
9795 */
9796static char *
9797pfgets(char *line, int len)
9798{
9799 char *p = line;
9800 int nleft = len;
9801 int c;
9802
9803 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009804 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009805 if (c == PEOF) {
9806 if (p == line)
9807 return NULL;
9808 break;
9809 }
9810 *p++ = c;
9811 if (c == '\n')
9812 break;
9813 }
9814 *p = '\0';
9815 return line;
9816}
9817
Eric Andersenc470f442003-07-28 09:56:35 +00009818/*
9819 * Undo the last call to pgetc. Only one character may be pushed back.
9820 * PEOF may be pushed back.
9821 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009822static void
Eric Andersenc470f442003-07-28 09:56:35 +00009823pungetc(void)
9824{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009825 g_parsefile->left_in_line++;
9826 g_parsefile->next_to_pgetc--;
9827 pgetc_debug("pushed back to %d:%p'%s'",
9828 g_parsefile->left_in_line,
9829 g_parsefile->next_to_pgetc,
9830 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009831}
9832
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009833/*
9834 * To handle the "." command, a stack of input files is used. Pushfile
9835 * adds a new entry to the stack and popfile restores the previous level.
9836 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009837static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009838pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009839{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009840 struct parsefile *pf;
9841
Denis Vlasenko597906c2008-02-20 16:38:54 +00009842 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009843 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009844 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009845 /*pf->strpush = NULL; - ckzalloc did it */
9846 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009847 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009848}
9849
9850static void
9851popfile(void)
9852{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009853 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009854
Denis Vlasenkob012b102007-02-19 22:43:01 +00009855 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009856 if (pf->pf_fd >= 0)
9857 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009858 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009859 while (pf->strpush)
9860 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009861 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009862 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009863 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009864}
9865
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009866/*
9867 * Return to top level.
9868 */
9869static void
9870popallfiles(void)
9871{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009872 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009873 popfile();
9874}
9875
9876/*
9877 * Close the file(s) that the shell is reading commands from. Called
9878 * after a fork is done.
9879 */
9880static void
9881closescript(void)
9882{
9883 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009884 if (g_parsefile->pf_fd > 0) {
9885 close(g_parsefile->pf_fd);
9886 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009887 }
9888}
9889
9890/*
9891 * Like setinputfile, but takes an open file descriptor. Call this with
9892 * interrupts off.
9893 */
9894static void
9895setinputfd(int fd, int push)
9896{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009897 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009898 if (push) {
9899 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009900 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009901 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009902 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009903 if (g_parsefile->buf == NULL)
9904 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009905 g_parsefile->left_in_buffer = 0;
9906 g_parsefile->left_in_line = 0;
9907 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009908}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009909
Eric Andersenc470f442003-07-28 09:56:35 +00009910/*
9911 * Set the input to take input from a file. If push is set, push the
9912 * old input onto the stack first.
9913 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009914static int
9915setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009916{
9917 int fd;
9918 int fd2;
9919
Denis Vlasenkob012b102007-02-19 22:43:01 +00009920 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009921 fd = open(fname, O_RDONLY);
9922 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009923 if (flags & INPUT_NOFILE_OK)
9924 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009925 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009926 }
Eric Andersenc470f442003-07-28 09:56:35 +00009927 if (fd < 10) {
9928 fd2 = copyfd(fd, 10);
9929 close(fd);
9930 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009931 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009932 fd = fd2;
9933 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009934 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009935 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009936 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009937 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009938}
9939
Eric Andersencb57d552001-06-28 07:25:16 +00009940/*
9941 * Like setinputfile, but takes input from a string.
9942 */
Eric Andersenc470f442003-07-28 09:56:35 +00009943static void
9944setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009945{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009946 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009947 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009948 g_parsefile->next_to_pgetc = string;
9949 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009950 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009951 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009952 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009953}
9954
9955
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009956/* ============ mail.c
9957 *
9958 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009959 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009960
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009961#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009962
Eric Andersencb57d552001-06-28 07:25:16 +00009963#define MAXMBOXES 10
9964
Eric Andersenc470f442003-07-28 09:56:35 +00009965/* times of mailboxes */
9966static time_t mailtime[MAXMBOXES];
9967/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009968static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009969
Eric Andersencb57d552001-06-28 07:25:16 +00009970/*
Eric Andersenc470f442003-07-28 09:56:35 +00009971 * Print appropriate message(s) if mail has arrived.
9972 * If mail_var_path_changed is set,
9973 * then the value of MAIL has mail_var_path_changed,
9974 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009975 */
Eric Andersenc470f442003-07-28 09:56:35 +00009976static void
9977chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009978{
Eric Andersencb57d552001-06-28 07:25:16 +00009979 const char *mpath;
9980 char *p;
9981 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009982 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009983 struct stackmark smark;
9984 struct stat statb;
9985
Eric Andersencb57d552001-06-28 07:25:16 +00009986 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009987 mpath = mpathset() ? mpathval() : mailval();
9988 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009989 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009990 if (p == NULL)
9991 break;
9992 if (*p == '\0')
9993 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009994 for (q = p; *q; q++)
9995 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009996#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009997 if (q[-1] != '/')
9998 abort();
9999#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010000 q[-1] = '\0'; /* delete trailing '/' */
10001 if (stat(p, &statb) < 0) {
10002 *mtp = 0;
10003 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010004 }
Eric Andersenc470f442003-07-28 09:56:35 +000010005 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10006 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010007 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010008 pathopt ? pathopt : "you have mail"
10009 );
10010 }
10011 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010012 }
Eric Andersenc470f442003-07-28 09:56:35 +000010013 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010014 popstackmark(&smark);
10015}
Eric Andersencb57d552001-06-28 07:25:16 +000010016
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010017static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010018changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010019{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010020 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010021}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010022
Denis Vlasenko131ae172007-02-18 13:00:19 +000010023#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010024
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010025
10026/* ============ ??? */
10027
Eric Andersencb57d552001-06-28 07:25:16 +000010028/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010029 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010030 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010031static void
10032setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010033{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010034 char **newparam;
10035 char **ap;
10036 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010037
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010038 for (nparam = 0; argv[nparam]; nparam++)
10039 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010040 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10041 while (*argv) {
10042 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010043 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010044 *ap = NULL;
10045 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010046 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010047 shellparam.nparam = nparam;
10048 shellparam.p = newparam;
10049#if ENABLE_ASH_GETOPTS
10050 shellparam.optind = 1;
10051 shellparam.optoff = -1;
10052#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010053}
10054
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010055/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010056 * Process shell options. The global variable argptr contains a pointer
10057 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010058 *
10059 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10060 * For a non-interactive shell, an error condition encountered
10061 * by a special built-in ... shall cause the shell to write a diagnostic message
10062 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010063 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010064 * ...
10065 * Utility syntax error (option or operand error) Shall exit
10066 * ...
10067 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10068 * we see that bash does not do that (set "finishes" with error code 1 instead,
10069 * and shell continues), and people rely on this behavior!
10070 * Testcase:
10071 * set -o barfoo 2>/dev/null
10072 * echo $?
10073 *
10074 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010075 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010076static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010077plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010078{
10079 int i;
10080
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010081 if (name) {
10082 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010083 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010084 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010085 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010086 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010087 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010088 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010089 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010090 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010091 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010092 if (val) {
10093 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10094 } else {
10095 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10096 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010097 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010098 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010099}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010100static void
10101setoption(int flag, int val)
10102{
10103 int i;
10104
10105 for (i = 0; i < NOPTS; i++) {
10106 if (optletters(i) == flag) {
10107 optlist[i] = val;
10108 return;
10109 }
10110 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010111 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010112 /* NOTREACHED */
10113}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010114static int
Eric Andersenc470f442003-07-28 09:56:35 +000010115options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010116{
10117 char *p;
10118 int val;
10119 int c;
10120
10121 if (cmdline)
10122 minusc = NULL;
10123 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010124 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010125 if (c != '-' && c != '+')
10126 break;
10127 argptr++;
10128 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010129 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010130 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010131 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010132 if (!cmdline) {
10133 /* "-" means turn off -x and -v */
10134 if (p[0] == '\0')
10135 xflag = vflag = 0;
10136 /* "--" means reset params */
10137 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010138 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010139 }
Eric Andersenc470f442003-07-28 09:56:35 +000010140 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010141 }
Eric Andersencb57d552001-06-28 07:25:16 +000010142 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010143 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010144 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010145 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010146 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010147 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010148 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010149 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010150 /* it already printed err message */
10151 return 1; /* error */
10152 }
Eric Andersencb57d552001-06-28 07:25:16 +000010153 if (*argptr)
10154 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010155 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10156 isloginsh = 1;
10157 /* bash does not accept +-login, we also won't */
10158 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010159 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010160 isloginsh = 1;
10161 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010162 } else {
10163 setoption(c, val);
10164 }
10165 }
10166 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010167 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010168}
10169
Eric Andersencb57d552001-06-28 07:25:16 +000010170/*
Eric Andersencb57d552001-06-28 07:25:16 +000010171 * The shift builtin command.
10172 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010173static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010174shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010175{
10176 int n;
10177 char **ap1, **ap2;
10178
10179 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010180 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010181 n = number(argv[1]);
10182 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010183 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010184 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010185 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010186 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010187 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010188 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010189 }
10190 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010191 while ((*ap2++ = *ap1++) != NULL)
10192 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010193#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010194 shellparam.optind = 1;
10195 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010196#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010197 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010198 return 0;
10199}
10200
Eric Andersencb57d552001-06-28 07:25:16 +000010201/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010202 * POSIX requires that 'set' (but not export or readonly) output the
10203 * variables in lexicographic order - by the locale's collating order (sigh).
10204 * Maybe we could keep them in an ordered balanced binary tree
10205 * instead of hashed lists.
10206 * For now just roll 'em through qsort for printing...
10207 */
10208static int
10209showvars(const char *sep_prefix, int on, int off)
10210{
10211 const char *sep;
10212 char **ep, **epend;
10213
10214 ep = listvars(on, off, &epend);
10215 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10216
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010217 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010218
10219 for (; ep < epend; ep++) {
10220 const char *p;
10221 const char *q;
10222
10223 p = strchrnul(*ep, '=');
10224 q = nullstr;
10225 if (*p)
10226 q = single_quote(++p);
10227 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10228 }
10229 return 0;
10230}
10231
10232/*
Eric Andersencb57d552001-06-28 07:25:16 +000010233 * The set command builtin.
10234 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010235static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010236setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010237{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010238 int retval;
10239
Denis Vlasenko68404f12008-03-17 09:00:54 +000010240 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010241 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010242 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010243 retval = 1;
10244 if (!options(0)) { /* if no parse error... */
10245 retval = 0;
10246 optschanged();
10247 if (*argptr != NULL) {
10248 setparam(argptr);
10249 }
Eric Andersencb57d552001-06-28 07:25:16 +000010250 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010251 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010252 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010253}
10254
Denis Vlasenko131ae172007-02-18 13:00:19 +000010255#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010256static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010257change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010258{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010259 uint32_t t;
10260
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010261 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010262 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010263 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010264 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010265 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010266 vrandom.flags &= ~VNOFUNC;
10267 } else {
10268 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010269 t = strtoul(value, NULL, 10);
10270 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010271 }
Eric Andersenef02f822004-03-11 13:34:24 +000010272}
Eric Andersen16767e22004-03-16 05:14:10 +000010273#endif
10274
Denis Vlasenko131ae172007-02-18 13:00:19 +000010275#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010276static int
Eric Andersenc470f442003-07-28 09:56:35 +000010277getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010278{
10279 char *p, *q;
10280 char c = '?';
10281 int done = 0;
10282 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010283 char s[12];
10284 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010285
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010286 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010287 return 1;
10288 optnext = optfirst + *param_optind - 1;
10289
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010290 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010291 p = NULL;
10292 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010293 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010294 if (p == NULL || *p == '\0') {
10295 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010296 p = *optnext;
10297 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010298 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010299 p = NULL;
10300 done = 1;
10301 goto out;
10302 }
10303 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010304 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010305 goto atend;
10306 }
10307
10308 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010309 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010310 if (*q == '\0') {
10311 if (optstr[0] == ':') {
10312 s[0] = c;
10313 s[1] = '\0';
10314 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010315 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010316 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010317 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010318 }
10319 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010320 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010321 }
10322 if (*++q == ':')
10323 q++;
10324 }
10325
10326 if (*++q == ':') {
10327 if (*p == '\0' && (p = *optnext) == NULL) {
10328 if (optstr[0] == ':') {
10329 s[0] = c;
10330 s[1] = '\0';
10331 err |= setvarsafe("OPTARG", s, 0);
10332 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010333 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010334 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010335 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010336 c = '?';
10337 }
Eric Andersenc470f442003-07-28 09:56:35 +000010338 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010339 }
10340
10341 if (p == *optnext)
10342 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010343 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010344 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010345 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010346 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010347 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010348 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010349 *param_optind = optnext - optfirst + 1;
10350 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010351 err |= setvarsafe("OPTIND", s, VNOFUNC);
10352 s[0] = c;
10353 s[1] = '\0';
10354 err |= setvarsafe(optvar, s, 0);
10355 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010356 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010357 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010358 flush_stdout_stderr();
10359 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010360 }
10361 return done;
10362}
Eric Andersenc470f442003-07-28 09:56:35 +000010363
10364/*
10365 * The getopts builtin. Shellparam.optnext points to the next argument
10366 * to be processed. Shellparam.optptr points to the next character to
10367 * be processed in the current argument. If shellparam.optnext is NULL,
10368 * then it's the first time getopts has been called.
10369 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010370static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010371getoptscmd(int argc, char **argv)
10372{
10373 char **optbase;
10374
10375 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010376 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010377 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010378 optbase = shellparam.p;
10379 if (shellparam.optind > shellparam.nparam + 1) {
10380 shellparam.optind = 1;
10381 shellparam.optoff = -1;
10382 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010383 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010384 optbase = &argv[3];
10385 if (shellparam.optind > argc - 2) {
10386 shellparam.optind = 1;
10387 shellparam.optoff = -1;
10388 }
10389 }
10390
10391 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010392 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010393}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010394#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010395
Eric Andersencb57d552001-06-28 07:25:16 +000010396
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010397/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010398
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010399struct heredoc {
10400 struct heredoc *next; /* next here document in list */
10401 union node *here; /* redirection node */
10402 char *eofmark; /* string indicating end of input */
10403 smallint striptabs; /* if set, strip leading tabs */
10404};
10405
10406static smallint tokpushback; /* last token pushed back */
10407static smallint parsebackquote; /* nonzero if we are inside backquotes */
10408static smallint quoteflag; /* set if (part of) last token was quoted */
10409static token_id_t lasttoken; /* last token read (integer id Txxx) */
10410static struct heredoc *heredoclist; /* list of here documents to read */
10411static char *wordtext; /* text of last word returned by readtoken */
10412static struct nodelist *backquotelist;
10413static union node *redirnode;
10414static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010415
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010416static const char *
10417tokname(char *buf, int tok)
10418{
10419 if (tok < TSEMI)
10420 return tokname_array[tok] + 1;
10421 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10422 return buf;
10423}
10424
10425/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010426 * Called when an unexpected token is read during the parse. The argument
10427 * is the token that is expected, or -1 if more than one type of token can
10428 * occur at this point.
10429 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010430static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010431static void
10432raise_error_unexpected_syntax(int token)
10433{
10434 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010435 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010436 int l;
10437
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010438 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010439 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010440 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010441 raise_error_syntax(msg);
10442 /* NOTREACHED */
10443}
Eric Andersencb57d552001-06-28 07:25:16 +000010444
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010445#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010446
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010447/* parsing is heavily cross-recursive, need these forward decls */
10448static union node *andor(void);
10449static union node *pipeline(void);
10450static union node *parse_command(void);
10451static void parseheredoc(void);
10452static char peektoken(void);
10453static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010454
Eric Andersenc470f442003-07-28 09:56:35 +000010455static union node *
10456list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010457{
10458 union node *n1, *n2, *n3;
10459 int tok;
10460
Eric Andersenc470f442003-07-28 09:56:35 +000010461 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10462 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010463 return NULL;
10464 n1 = NULL;
10465 for (;;) {
10466 n2 = andor();
10467 tok = readtoken();
10468 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010469 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010470 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010471 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010472 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010473 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010474 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010475 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010476 n2 = n3;
10477 }
10478 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010479 }
10480 }
10481 if (n1 == NULL) {
10482 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010483 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010484 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010485 n3->type = NSEMI;
10486 n3->nbinary.ch1 = n1;
10487 n3->nbinary.ch2 = n2;
10488 n1 = n3;
10489 }
10490 switch (tok) {
10491 case TBACKGND:
10492 case TSEMI:
10493 tok = readtoken();
10494 /* fall through */
10495 case TNL:
10496 if (tok == TNL) {
10497 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010498 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010499 return n1;
10500 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010501 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010502 }
Eric Andersenc470f442003-07-28 09:56:35 +000010503 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010504 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010505 return n1;
10506 break;
10507 case TEOF:
10508 if (heredoclist)
10509 parseheredoc();
10510 else
Eric Andersenc470f442003-07-28 09:56:35 +000010511 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010512 return n1;
10513 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010514 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010515 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010516 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010517 return n1;
10518 }
10519 }
10520}
10521
Eric Andersenc470f442003-07-28 09:56:35 +000010522static union node *
10523andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010524{
Eric Andersencb57d552001-06-28 07:25:16 +000010525 union node *n1, *n2, *n3;
10526 int t;
10527
Eric Andersencb57d552001-06-28 07:25:16 +000010528 n1 = pipeline();
10529 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010530 t = readtoken();
10531 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010532 t = NAND;
10533 } else if (t == TOR) {
10534 t = NOR;
10535 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010536 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010537 return n1;
10538 }
Eric Andersenc470f442003-07-28 09:56:35 +000010539 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010540 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010541 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010542 n3->type = t;
10543 n3->nbinary.ch1 = n1;
10544 n3->nbinary.ch2 = n2;
10545 n1 = n3;
10546 }
10547}
10548
Eric Andersenc470f442003-07-28 09:56:35 +000010549static union node *
10550pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010551{
Eric Andersencb57d552001-06-28 07:25:16 +000010552 union node *n1, *n2, *pipenode;
10553 struct nodelist *lp, *prev;
10554 int negate;
10555
10556 negate = 0;
10557 TRACE(("pipeline: entered\n"));
10558 if (readtoken() == TNOT) {
10559 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010560 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010561 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010562 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010563 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010564 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010565 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010566 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010567 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010568 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010569 pipenode->npipe.cmdlist = lp;
10570 lp->n = n1;
10571 do {
10572 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010573 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010574 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010575 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010576 prev->next = lp;
10577 } while (readtoken() == TPIPE);
10578 lp->next = NULL;
10579 n1 = pipenode;
10580 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010581 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010582 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010583 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010584 n2->type = NNOT;
10585 n2->nnot.com = n1;
10586 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010587 }
10588 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010589}
10590
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010591static union node *
10592makename(void)
10593{
10594 union node *n;
10595
Denis Vlasenko597906c2008-02-20 16:38:54 +000010596 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010597 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010598 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010599 n->narg.text = wordtext;
10600 n->narg.backquote = backquotelist;
10601 return n;
10602}
10603
10604static void
10605fixredir(union node *n, const char *text, int err)
10606{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010607 int fd;
10608
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010609 TRACE(("Fix redir %s %d\n", text, err));
10610 if (!err)
10611 n->ndup.vname = NULL;
10612
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010613 fd = bb_strtou(text, NULL, 10);
10614 if (!errno && fd >= 0)
10615 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010616 else if (LONE_DASH(text))
10617 n->ndup.dupfd = -1;
10618 else {
10619 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010620 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010621 n->ndup.vname = makename();
10622 }
10623}
10624
10625/*
10626 * Returns true if the text contains nothing to expand (no dollar signs
10627 * or backquotes).
10628 */
10629static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010630noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010631{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010632 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010633
Denys Vlasenkocd716832009-11-28 22:14:02 +010010634 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010635 if (c == CTLQUOTEMARK)
10636 continue;
10637 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010638 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010639 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 return 0;
10641 }
10642 return 1;
10643}
10644
10645static void
10646parsefname(void)
10647{
10648 union node *n = redirnode;
10649
10650 if (readtoken() != TWORD)
10651 raise_error_unexpected_syntax(-1);
10652 if (n->type == NHERE) {
10653 struct heredoc *here = heredoc;
10654 struct heredoc *p;
10655 int i;
10656
10657 if (quoteflag == 0)
10658 n->type = NXHERE;
10659 TRACE(("Here document %d\n", n->type));
10660 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010661 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010662 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010663 here->eofmark = wordtext;
10664 here->next = NULL;
10665 if (heredoclist == NULL)
10666 heredoclist = here;
10667 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010668 for (p = heredoclist; p->next; p = p->next)
10669 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010670 p->next = here;
10671 }
10672 } else if (n->type == NTOFD || n->type == NFROMFD) {
10673 fixredir(n, wordtext, 0);
10674 } else {
10675 n->nfile.fname = makename();
10676 }
10677}
Eric Andersencb57d552001-06-28 07:25:16 +000010678
Eric Andersenc470f442003-07-28 09:56:35 +000010679static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010680simplecmd(void)
10681{
10682 union node *args, **app;
10683 union node *n = NULL;
10684 union node *vars, **vpp;
10685 union node **rpp, *redir;
10686 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010687#if ENABLE_ASH_BASH_COMPAT
10688 smallint double_brackets_flag = 0;
10689#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010690
10691 args = NULL;
10692 app = &args;
10693 vars = NULL;
10694 vpp = &vars;
10695 redir = NULL;
10696 rpp = &redir;
10697
10698 savecheckkwd = CHKALIAS;
10699 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010700 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010701 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010702 t = readtoken();
10703 switch (t) {
10704#if ENABLE_ASH_BASH_COMPAT
10705 case TAND: /* "&&" */
10706 case TOR: /* "||" */
10707 if (!double_brackets_flag) {
10708 tokpushback = 1;
10709 goto out;
10710 }
10711 wordtext = (char *) (t == TAND ? "-a" : "-o");
10712#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010713 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010714 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010715 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010716 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010717 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010718#if ENABLE_ASH_BASH_COMPAT
10719 if (strcmp("[[", wordtext) == 0)
10720 double_brackets_flag = 1;
10721 else if (strcmp("]]", wordtext) == 0)
10722 double_brackets_flag = 0;
10723#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010724 n->narg.backquote = backquotelist;
10725 if (savecheckkwd && isassignment(wordtext)) {
10726 *vpp = n;
10727 vpp = &n->narg.next;
10728 } else {
10729 *app = n;
10730 app = &n->narg.next;
10731 savecheckkwd = 0;
10732 }
10733 break;
10734 case TREDIR:
10735 *rpp = n = redirnode;
10736 rpp = &n->nfile.next;
10737 parsefname(); /* read name of redirection file */
10738 break;
10739 case TLP:
10740 if (args && app == &args->narg.next
10741 && !vars && !redir
10742 ) {
10743 struct builtincmd *bcmd;
10744 const char *name;
10745
10746 /* We have a function */
10747 if (readtoken() != TRP)
10748 raise_error_unexpected_syntax(TRP);
10749 name = n->narg.text;
10750 if (!goodname(name)
10751 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10752 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010753 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010754 }
10755 n->type = NDEFUN;
10756 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10757 n->narg.next = parse_command();
10758 return n;
10759 }
10760 /* fall through */
10761 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010762 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010763 goto out;
10764 }
10765 }
10766 out:
10767 *app = NULL;
10768 *vpp = NULL;
10769 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010770 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010771 n->type = NCMD;
10772 n->ncmd.args = args;
10773 n->ncmd.assign = vars;
10774 n->ncmd.redirect = redir;
10775 return n;
10776}
10777
10778static union node *
10779parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010780{
Eric Andersencb57d552001-06-28 07:25:16 +000010781 union node *n1, *n2;
10782 union node *ap, **app;
10783 union node *cp, **cpp;
10784 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010785 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010786 int t;
10787
10788 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010789 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010790
Eric Andersencb57d552001-06-28 07:25:16 +000010791 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010792 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010793 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010794 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010795 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010796 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010797 n1->type = NIF;
10798 n1->nif.test = list(0);
10799 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010800 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010801 n1->nif.ifpart = list(0);
10802 n2 = n1;
10803 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010804 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010805 n2 = n2->nif.elsepart;
10806 n2->type = NIF;
10807 n2->nif.test = list(0);
10808 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010809 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010810 n2->nif.ifpart = list(0);
10811 }
10812 if (lasttoken == TELSE)
10813 n2->nif.elsepart = list(0);
10814 else {
10815 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010816 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010817 }
Eric Andersenc470f442003-07-28 09:56:35 +000010818 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010819 break;
10820 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010821 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010822 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010823 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010824 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010825 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 got = readtoken();
10827 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010828 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010829 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010830 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010831 }
10832 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010833 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010834 break;
10835 }
10836 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010837 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010838 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010839 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010840 n1->type = NFOR;
10841 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010842 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010843 if (readtoken() == TIN) {
10844 app = &ap;
10845 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010846 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010847 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010848 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010849 n2->narg.text = wordtext;
10850 n2->narg.backquote = backquotelist;
10851 *app = n2;
10852 app = &n2->narg.next;
10853 }
10854 *app = NULL;
10855 n1->nfor.args = ap;
10856 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010857 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010858 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010859 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010860 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010861 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010862 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010863 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010864 n1->nfor.args = n2;
10865 /*
10866 * Newline or semicolon here is optional (but note
10867 * that the original Bourne shell only allowed NL).
10868 */
10869 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010870 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010871 }
Eric Andersenc470f442003-07-28 09:56:35 +000010872 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010873 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010874 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010875 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010876 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 break;
10878 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010879 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010880 n1->type = NCASE;
10881 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010882 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010883 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010884 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010885 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010886 n2->narg.text = wordtext;
10887 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010888 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010889 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010890 } while (readtoken() == TNL);
10891 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010892 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010893 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010894 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010895 checkkwd = CHKNL | CHKKWD;
10896 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010897 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010898 if (lasttoken == TLP)
10899 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010900 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010901 cp->type = NCLIST;
10902 app = &cp->nclist.pattern;
10903 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010904 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010905 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010906 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010907 ap->narg.text = wordtext;
10908 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010909 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010910 break;
10911 app = &ap->narg.next;
10912 readtoken();
10913 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010914 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010915 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010916 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010917 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010918
Eric Andersenc470f442003-07-28 09:56:35 +000010919 cpp = &cp->nclist.next;
10920
10921 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010922 t = readtoken();
10923 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010924 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010925 raise_error_unexpected_syntax(TENDCASE);
10926 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010927 }
Eric Andersenc470f442003-07-28 09:56:35 +000010928 }
Eric Andersencb57d552001-06-28 07:25:16 +000010929 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010930 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010932 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010933 n1->type = NSUBSHELL;
10934 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010935 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010936 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010937 break;
10938 case TBEGIN:
10939 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010940 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010941 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010942 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010943 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010944 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010945 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010946 }
10947
Eric Andersenc470f442003-07-28 09:56:35 +000010948 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010949 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010950
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010951 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010952 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010953 checkkwd = CHKKWD | CHKALIAS;
10954 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010955 while (readtoken() == TREDIR) {
10956 *rpp = n2 = redirnode;
10957 rpp = &n2->nfile.next;
10958 parsefname();
10959 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010960 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010961 *rpp = NULL;
10962 if (redir) {
10963 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010964 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010965 n2->type = NREDIR;
10966 n2->nredir.n = n1;
10967 n1 = n2;
10968 }
10969 n1->nredir.redirect = redir;
10970 }
Eric Andersencb57d552001-06-28 07:25:16 +000010971 return n1;
10972}
10973
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010974#if ENABLE_ASH_BASH_COMPAT
10975static int decode_dollar_squote(void)
10976{
10977 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10978 int c, cnt;
10979 char *p;
10980 char buf[4];
10981
10982 c = pgetc();
10983 p = strchr(C_escapes, c);
10984 if (p) {
10985 buf[0] = c;
10986 p = buf;
10987 cnt = 3;
10988 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10989 do {
10990 c = pgetc();
10991 *++p = c;
10992 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10993 pungetc();
10994 } else if (c == 'x') { /* \xHH */
10995 do {
10996 c = pgetc();
10997 *++p = c;
10998 } while (isxdigit(c) && --cnt);
10999 pungetc();
11000 if (cnt == 3) { /* \x but next char is "bad" */
11001 c = 'x';
11002 goto unrecognized;
11003 }
11004 } else { /* simple seq like \\ or \t */
11005 p++;
11006 }
11007 *p = '\0';
11008 p = buf;
11009 c = bb_process_escape_sequence((void*)&p);
11010 } else { /* unrecognized "\z": print both chars unless ' or " */
11011 if (c != '\'' && c != '"') {
11012 unrecognized:
11013 c |= 0x100; /* "please encode \, then me" */
11014 }
11015 }
11016 return c;
11017}
11018#endif
11019
Eric Andersencb57d552001-06-28 07:25:16 +000011020/*
11021 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11022 * is not NULL, read a here document. In the latter case, eofmark is the
11023 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011024 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011025 * is the first character of the input token or document.
11026 *
11027 * Because C does not have internal subroutines, I have simulated them
11028 * using goto's to implement the subroutine linkage. The following macros
11029 * will run code that appears at the end of readtoken1.
11030 */
Eric Andersen2870d962001-07-02 17:27:21 +000011031#define CHECKEND() {goto checkend; checkend_return:;}
11032#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11033#define PARSESUB() {goto parsesub; parsesub_return:;}
11034#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11035#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11036#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011037static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011038readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011039{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011040 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011041 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011042 char *out;
11043 int len;
11044 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011045 struct nodelist *bqlist;
11046 smallint quotef;
11047 smallint dblquote;
11048 smallint oldstyle;
11049 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011050#if ENABLE_ASH_EXPAND_PRMT
11051 smallint pssyntax; /* we are expanding a prompt string */
11052#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011053 int varnest; /* levels of variables expansion */
11054 int arinest; /* levels of arithmetic expansion */
11055 int parenlevel; /* levels of parens in arithmetic */
11056 int dqvarnest; /* levels of variables expansion within double quotes */
11057
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011058 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011059
Eric Andersencb57d552001-06-28 07:25:16 +000011060#if __GNUC__
11061 /* Avoid longjmp clobbering */
11062 (void) &out;
11063 (void) &quotef;
11064 (void) &dblquote;
11065 (void) &varnest;
11066 (void) &arinest;
11067 (void) &parenlevel;
11068 (void) &dqvarnest;
11069 (void) &oldstyle;
11070 (void) &prevsyntax;
11071 (void) &syntax;
11072#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011073 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011074 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011075 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011076 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011077#if ENABLE_ASH_EXPAND_PRMT
11078 pssyntax = (syntax == PSSYNTAX);
11079 if (pssyntax)
11080 syntax = DQSYNTAX;
11081#endif
11082 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011083 varnest = 0;
11084 arinest = 0;
11085 parenlevel = 0;
11086 dqvarnest = 0;
11087
11088 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011089 loop:
11090 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011091 CHECKEND(); /* set c to PEOF if at end of here document */
11092 for (;;) { /* until end of line or end of word */
11093 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11094 switch (SIT(c, syntax)) {
11095 case CNL: /* '\n' */
11096 if (syntax == BASESYNTAX)
11097 goto endword; /* exit outer loop */
11098 USTPUTC(c, out);
11099 g_parsefile->linno++;
11100 setprompt_if(doprompt, 2);
11101 c = pgetc();
11102 goto loop; /* continue outer loop */
11103 case CWORD:
11104 USTPUTC(c, out);
11105 break;
11106 case CCTL:
11107 if (eofmark == NULL || dblquote)
11108 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011109#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011110 if (c == '\\' && bash_dollar_squote) {
11111 c = decode_dollar_squote();
11112 if (c & 0x100) {
11113 USTPUTC('\\', out);
11114 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011115 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011116 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011117#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011118 USTPUTC(c, out);
11119 break;
11120 case CBACK: /* backslash */
11121 c = pgetc_without_PEOA();
11122 if (c == PEOF) {
11123 USTPUTC(CTLESC, out);
11124 USTPUTC('\\', out);
11125 pungetc();
11126 } else if (c == '\n') {
11127 setprompt_if(doprompt, 2);
11128 } else {
11129#if ENABLE_ASH_EXPAND_PRMT
11130 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011131 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011132 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011133 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011134#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011135 /* Backslash is retained if we are in "str" and next char isn't special */
11136 if (dblquote
11137 && c != '\\'
11138 && c != '`'
11139 && c != '$'
11140 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011141 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011142 USTPUTC(CTLESC, out);
11143 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011144 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011145 if (SIT(c, SQSYNTAX) == CCTL)
11146 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011147 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011148 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011149 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011150 break;
11151 case CSQUOTE:
11152 syntax = SQSYNTAX;
11153 quotemark:
11154 if (eofmark == NULL) {
11155 USTPUTC(CTLQUOTEMARK, out);
11156 }
11157 break;
11158 case CDQUOTE:
11159 syntax = DQSYNTAX;
11160 dblquote = 1;
11161 goto quotemark;
11162 case CENDQUOTE:
11163 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11164 if (eofmark != NULL && arinest == 0
11165 && varnest == 0
11166 ) {
11167 USTPUTC(c, out);
11168 } else {
11169 if (dqvarnest == 0) {
11170 syntax = BASESYNTAX;
11171 dblquote = 0;
11172 }
11173 quotef = 1;
11174 goto quotemark;
11175 }
11176 break;
11177 case CVAR: /* '$' */
11178 PARSESUB(); /* parse substitution */
11179 break;
11180 case CENDVAR: /* '}' */
11181 if (varnest > 0) {
11182 varnest--;
11183 if (dqvarnest > 0) {
11184 dqvarnest--;
11185 }
11186 c = CTLENDVAR;
11187 }
11188 USTPUTC(c, out);
11189 break;
11190#if ENABLE_SH_MATH_SUPPORT
11191 case CLP: /* '(' in arithmetic */
11192 parenlevel++;
11193 USTPUTC(c, out);
11194 break;
11195 case CRP: /* ')' in arithmetic */
11196 if (parenlevel > 0) {
11197 parenlevel--;
11198 } else {
11199 if (pgetc() == ')') {
11200 if (--arinest == 0) {
11201 syntax = prevsyntax;
11202 dblquote = (syntax == DQSYNTAX);
11203 c = CTLENDARI;
11204 }
11205 } else {
11206 /*
11207 * unbalanced parens
11208 * (don't 2nd guess - no error)
11209 */
11210 pungetc();
11211 }
11212 }
11213 USTPUTC(c, out);
11214 break;
11215#endif
11216 case CBQUOTE: /* '`' */
11217 PARSEBACKQOLD();
11218 break;
11219 case CENDFILE:
11220 goto endword; /* exit outer loop */
11221 case CIGN:
11222 break;
11223 default:
11224 if (varnest == 0) {
11225#if ENABLE_ASH_BASH_COMPAT
11226 if (c == '&') {
11227 if (pgetc() == '>')
11228 c = 0x100 + '>'; /* flag &> */
11229 pungetc();
11230 }
11231#endif
11232 goto endword; /* exit outer loop */
11233 }
11234 IF_ASH_ALIAS(if (c != PEOA))
11235 USTPUTC(c, out);
11236 }
11237 c = pgetc_fast();
11238 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011239 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011240
Mike Frysinger98c52642009-04-02 10:02:37 +000011241#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011242 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011243 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011244#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011245 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011246 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011247 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011248 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011249 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011250 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011251 }
11252 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011253 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011254 out = stackblock();
11255 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011256 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011257 && quotef == 0
11258 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011259 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011260 PARSEREDIR(); /* passed as params: out, c */
11261 lasttoken = TREDIR;
11262 return lasttoken;
11263 }
11264 /* else: non-number X seen, interpret it
11265 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011266 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011267 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011268 }
11269 quoteflag = quotef;
11270 backquotelist = bqlist;
11271 grabstackblock(len);
11272 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011273 lasttoken = TWORD;
11274 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011275/* end of readtoken routine */
11276
Eric Andersencb57d552001-06-28 07:25:16 +000011277/*
11278 * Check to see whether we are at the end of the here document. When this
11279 * is called, c is set to the first character of the next input line. If
11280 * we are at the end of the here document, this routine sets the c to PEOF.
11281 */
Eric Andersenc470f442003-07-28 09:56:35 +000011282checkend: {
11283 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011284#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011285 if (c == PEOA)
11286 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011287#endif
11288 if (striptabs) {
11289 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011290 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011291 }
Eric Andersenc470f442003-07-28 09:56:35 +000011292 }
11293 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011294 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011295 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011296
Eric Andersenc470f442003-07-28 09:56:35 +000011297 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011298 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11299 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011300 if (*p == '\n' && *q == '\0') {
11301 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011302 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011303 needprompt = doprompt;
11304 } else {
11305 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011306 }
11307 }
11308 }
11309 }
Eric Andersenc470f442003-07-28 09:56:35 +000011310 goto checkend_return;
11311}
Eric Andersencb57d552001-06-28 07:25:16 +000011312
Eric Andersencb57d552001-06-28 07:25:16 +000011313/*
11314 * Parse a redirection operator. The variable "out" points to a string
11315 * specifying the fd to be redirected. The variable "c" contains the
11316 * first character of the redirection operator.
11317 */
Eric Andersenc470f442003-07-28 09:56:35 +000011318parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011319 /* out is already checked to be a valid number or "" */
11320 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011321 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011322
Denis Vlasenko597906c2008-02-20 16:38:54 +000011323 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011324 if (c == '>') {
11325 np->nfile.fd = 1;
11326 c = pgetc();
11327 if (c == '>')
11328 np->type = NAPPEND;
11329 else if (c == '|')
11330 np->type = NCLOBBER;
11331 else if (c == '&')
11332 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011333 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011334 else {
11335 np->type = NTO;
11336 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011337 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011338 }
11339#if ENABLE_ASH_BASH_COMPAT
11340 else if (c == 0x100 + '>') { /* this flags &> redirection */
11341 np->nfile.fd = 1;
11342 pgetc(); /* this is '>', no need to check */
11343 np->type = NTO2;
11344 }
11345#endif
11346 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011347 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011348 c = pgetc();
11349 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011350 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011351 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011352 np = stzalloc(sizeof(struct nhere));
11353 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011354 }
11355 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011356 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011357 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011358 c = pgetc();
11359 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011360 heredoc->striptabs = 1;
11361 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011362 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011363 pungetc();
11364 }
11365 break;
11366
11367 case '&':
11368 np->type = NFROMFD;
11369 break;
11370
11371 case '>':
11372 np->type = NFROMTO;
11373 break;
11374
11375 default:
11376 np->type = NFROM;
11377 pungetc();
11378 break;
11379 }
Eric Andersencb57d552001-06-28 07:25:16 +000011380 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011381 if (fd >= 0)
11382 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011383 redirnode = np;
11384 goto parseredir_return;
11385}
Eric Andersencb57d552001-06-28 07:25:16 +000011386
Eric Andersencb57d552001-06-28 07:25:16 +000011387/*
11388 * Parse a substitution. At this point, we have read the dollar sign
11389 * and nothing else.
11390 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011391
11392/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11393 * (assuming ascii char codes, as the original implementation did) */
11394#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011395 (((unsigned)(c) - 33 < 32) \
11396 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011397parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011398 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011399 int typeloc;
11400 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011401
Eric Andersenc470f442003-07-28 09:56:35 +000011402 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011403 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011404 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011405 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011406#if ENABLE_ASH_BASH_COMPAT
11407 if (c == '\'')
11408 bash_dollar_squote = 1;
11409 else
11410#endif
11411 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011412 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011413 } else if (c == '(') {
11414 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011415 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011416#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011417 PARSEARITH();
11418#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011419 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011420#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011421 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011422 pungetc();
11423 PARSEBACKQNEW();
11424 }
11425 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011426 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011427 USTPUTC(CTLVAR, out);
11428 typeloc = out - (char *)stackblock();
11429 USTPUTC(VSNORMAL, out);
11430 subtype = VSNORMAL;
11431 if (c == '{') {
11432 c = pgetc();
11433 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011434 c = pgetc();
11435 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011436 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011437 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011438 subtype = VSLENGTH; /* ${#VAR} */
11439 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011440 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011441 }
Eric Andersenc470f442003-07-28 09:56:35 +000011442 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011443 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011444 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011445 do {
11446 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011447 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011448 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011449 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011450 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011451 do {
11452 STPUTC(c, out);
11453 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011454 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011455 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011456 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011457 USTPUTC(c, out);
11458 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011459 } else {
11460 badsub:
11461 raise_error_syntax("bad substitution");
11462 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011463 if (c != '}' && subtype == VSLENGTH) {
11464 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011465 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011466 }
Eric Andersencb57d552001-06-28 07:25:16 +000011467
Eric Andersenc470f442003-07-28 09:56:35 +000011468 STPUTC('=', out);
11469 flags = 0;
11470 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011471 /* ${VAR...} but not $VAR or ${#VAR} */
11472 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011473 switch (c) {
11474 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011475 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011476#if ENABLE_ASH_BASH_COMPAT
11477 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011478//TODO: support more general format ${v:EXPR:EXPR},
11479// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011480 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011481 pungetc();
11482 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011483 }
11484#endif
11485 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011486 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011487 default: {
11488 static const char types[] ALIGN1 = "}-+?=";
11489 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011490 if (p == NULL)
11491 goto badsub;
11492 subtype = p - types + VSNORMAL;
11493 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011494 }
Eric Andersenc470f442003-07-28 09:56:35 +000011495 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011496 case '#': {
11497 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011498 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011499 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011500 if (c != cc)
11501 goto do_pungetc;
11502 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011503 break;
11504 }
11505#if ENABLE_ASH_BASH_COMPAT
11506 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011507 /* ${v/[/]pattern/repl} */
11508//TODO: encode pattern and repl separately.
11509// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011510 subtype = VSREPLACE;
11511 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011512 if (c != '/')
11513 goto do_pungetc;
11514 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011515 break;
11516#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011517 }
Eric Andersenc470f442003-07-28 09:56:35 +000011518 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011519 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011520 pungetc();
11521 }
11522 if (dblquote || arinest)
11523 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011524 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011525 if (subtype != VSNORMAL) {
11526 varnest++;
11527 if (dblquote || arinest) {
11528 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011529 }
11530 }
11531 }
Eric Andersenc470f442003-07-28 09:56:35 +000011532 goto parsesub_return;
11533}
Eric Andersencb57d552001-06-28 07:25:16 +000011534
Eric Andersencb57d552001-06-28 07:25:16 +000011535/*
11536 * Called to parse command substitutions. Newstyle is set if the command
11537 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11538 * list of commands (passed by reference), and savelen is the number of
11539 * characters on the top of the stack which must be preserved.
11540 */
Eric Andersenc470f442003-07-28 09:56:35 +000011541parsebackq: {
11542 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011543 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011544 union node *n;
11545 char *volatile str;
11546 struct jmploc jmploc;
11547 struct jmploc *volatile savehandler;
11548 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011549 smallint saveprompt = 0;
11550
Eric Andersencb57d552001-06-28 07:25:16 +000011551#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011552 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011553#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011554 savepbq = parsebackquote;
11555 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011556 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011557 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011558 exception_handler = savehandler;
11559 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011560 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011561 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011562 str = NULL;
11563 savelen = out - (char *)stackblock();
11564 if (savelen > 0) {
11565 str = ckmalloc(savelen);
11566 memcpy(str, stackblock(), savelen);
11567 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011568 savehandler = exception_handler;
11569 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011570 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011571 if (oldstyle) {
11572 /* We must read until the closing backquote, giving special
11573 treatment to some slashes, and then push the string and
11574 reread it as input, interpreting it normally. */
11575 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011576 size_t psavelen;
11577 char *pstr;
11578
Eric Andersenc470f442003-07-28 09:56:35 +000011579 STARTSTACKSTR(pout);
11580 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011581 int pc;
11582
11583 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011584 pc = pgetc();
11585 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011586 case '`':
11587 goto done;
11588
11589 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011590 pc = pgetc();
11591 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011592 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011593 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011594 /*
11595 * If eating a newline, avoid putting
11596 * the newline into the new character
11597 * stream (via the STPUTC after the
11598 * switch).
11599 */
11600 continue;
11601 }
11602 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011603 && (!dblquote || pc != '"')
11604 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011605 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011606 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011607 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011608 break;
11609 }
11610 /* fall through */
11611
11612 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011613 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011614 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011615 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011616
11617 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011618 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011619 needprompt = doprompt;
11620 break;
11621
11622 default:
11623 break;
11624 }
11625 STPUTC(pc, pout);
11626 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011627 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011628 STPUTC('\0', pout);
11629 psavelen = pout - (char *)stackblock();
11630 if (psavelen > 0) {
11631 pstr = grabstackstr(pout);
11632 setinputstring(pstr);
11633 }
11634 }
11635 nlpp = &bqlist;
11636 while (*nlpp)
11637 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011638 *nlpp = stzalloc(sizeof(**nlpp));
11639 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011640 parsebackquote = oldstyle;
11641
11642 if (oldstyle) {
11643 saveprompt = doprompt;
11644 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011645 }
11646
Eric Andersenc470f442003-07-28 09:56:35 +000011647 n = list(2);
11648
11649 if (oldstyle)
11650 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011651 else if (readtoken() != TRP)
11652 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011653
11654 (*nlpp)->n = n;
11655 if (oldstyle) {
11656 /*
11657 * Start reading from old file again, ignoring any pushed back
11658 * tokens left from the backquote parsing
11659 */
11660 popfile();
11661 tokpushback = 0;
11662 }
11663 while (stackblocksize() <= savelen)
11664 growstackblock();
11665 STARTSTACKSTR(out);
11666 if (str) {
11667 memcpy(out, str, savelen);
11668 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011669 INT_OFF;
11670 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011671 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011672 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011673 }
11674 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011675 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011676 if (arinest || dblquote)
11677 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11678 else
11679 USTPUTC(CTLBACKQ, out);
11680 if (oldstyle)
11681 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011682 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011683}
11684
Mike Frysinger98c52642009-04-02 10:02:37 +000011685#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011686/*
11687 * Parse an arithmetic expansion (indicate start of one and set state)
11688 */
Eric Andersenc470f442003-07-28 09:56:35 +000011689parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011690 if (++arinest == 1) {
11691 prevsyntax = syntax;
11692 syntax = ARISYNTAX;
11693 USTPUTC(CTLARI, out);
11694 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011695 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011696 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011697 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011698 } else {
11699 /*
11700 * we collapse embedded arithmetic expansion to
11701 * parenthesis, which should be equivalent
11702 */
11703 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011704 }
Eric Andersenc470f442003-07-28 09:56:35 +000011705 goto parsearith_return;
11706}
11707#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011708
Eric Andersenc470f442003-07-28 09:56:35 +000011709} /* end of readtoken */
11710
Eric Andersencb57d552001-06-28 07:25:16 +000011711/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011712 * Read the next input token.
11713 * If the token is a word, we set backquotelist to the list of cmds in
11714 * backquotes. We set quoteflag to true if any part of the word was
11715 * quoted.
11716 * If the token is TREDIR, then we set redirnode to a structure containing
11717 * the redirection.
11718 * In all cases, the variable startlinno is set to the number of the line
11719 * on which the token starts.
11720 *
11721 * [Change comment: here documents and internal procedures]
11722 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11723 * word parsing code into a separate routine. In this case, readtoken
11724 * doesn't need to have any internal procedures, but parseword does.
11725 * We could also make parseoperator in essence the main routine, and
11726 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011727 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011728#define NEW_xxreadtoken
11729#ifdef NEW_xxreadtoken
11730/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011731static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011732 '\n', '(', ')', /* singles */
11733 '&', '|', ';', /* doubles */
11734 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011735};
Eric Andersencb57d552001-06-28 07:25:16 +000011736
Denis Vlasenko834dee72008-10-07 09:18:30 +000011737#define xxreadtoken_singles 3
11738#define xxreadtoken_doubles 3
11739
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011740static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 TNL, TLP, TRP, /* only single occurrence allowed */
11742 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11743 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011744 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011745};
11746
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011747static int
11748xxreadtoken(void)
11749{
11750 int c;
11751
11752 if (tokpushback) {
11753 tokpushback = 0;
11754 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011755 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011756 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011757 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011758 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011759 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011760 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011761 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011762
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011763 if (c == '#') {
11764 while ((c = pgetc()) != '\n' && c != PEOF)
11765 continue;
11766 pungetc();
11767 } else if (c == '\\') {
11768 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011769 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011770 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011771 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011772 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011773 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011774 } else {
11775 const char *p;
11776
11777 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11778 if (c != PEOF) {
11779 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011780 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011781 needprompt = doprompt;
11782 }
11783
11784 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011785 if (p == NULL)
11786 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011787
Denis Vlasenko834dee72008-10-07 09:18:30 +000011788 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11789 int cc = pgetc();
11790 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011791 p += xxreadtoken_doubles + 1;
11792 } else {
11793 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011794#if ENABLE_ASH_BASH_COMPAT
11795 if (c == '&' && cc == '>') /* &> */
11796 break; /* return readtoken1(...) */
11797#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011798 }
11799 }
11800 }
11801 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11802 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011803 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011804 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011805
11806 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011807}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011808#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011809#define RETURN(token) return lasttoken = token
11810static int
11811xxreadtoken(void)
11812{
11813 int c;
11814
11815 if (tokpushback) {
11816 tokpushback = 0;
11817 return lasttoken;
11818 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011819 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011820 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011821 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011822 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011823 switch (c) {
11824 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011825 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011826 continue;
11827 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011828 while ((c = pgetc()) != '\n' && c != PEOF)
11829 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011830 pungetc();
11831 continue;
11832 case '\\':
11833 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011834 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011835 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011836 continue;
11837 }
11838 pungetc();
11839 goto breakloop;
11840 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011841 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011842 needprompt = doprompt;
11843 RETURN(TNL);
11844 case PEOF:
11845 RETURN(TEOF);
11846 case '&':
11847 if (pgetc() == '&')
11848 RETURN(TAND);
11849 pungetc();
11850 RETURN(TBACKGND);
11851 case '|':
11852 if (pgetc() == '|')
11853 RETURN(TOR);
11854 pungetc();
11855 RETURN(TPIPE);
11856 case ';':
11857 if (pgetc() == ';')
11858 RETURN(TENDCASE);
11859 pungetc();
11860 RETURN(TSEMI);
11861 case '(':
11862 RETURN(TLP);
11863 case ')':
11864 RETURN(TRP);
11865 default:
11866 goto breakloop;
11867 }
11868 }
11869 breakloop:
11870 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11871#undef RETURN
11872}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011873#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011874
11875static int
11876readtoken(void)
11877{
11878 int t;
11879#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011880 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011881#endif
11882
11883#if ENABLE_ASH_ALIAS
11884 top:
11885#endif
11886
11887 t = xxreadtoken();
11888
11889 /*
11890 * eat newlines
11891 */
11892 if (checkkwd & CHKNL) {
11893 while (t == TNL) {
11894 parseheredoc();
11895 t = xxreadtoken();
11896 }
11897 }
11898
11899 if (t != TWORD || quoteflag) {
11900 goto out;
11901 }
11902
11903 /*
11904 * check for keywords
11905 */
11906 if (checkkwd & CHKKWD) {
11907 const char *const *pp;
11908
11909 pp = findkwd(wordtext);
11910 if (pp) {
11911 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011912 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011913 goto out;
11914 }
11915 }
11916
11917 if (checkkwd & CHKALIAS) {
11918#if ENABLE_ASH_ALIAS
11919 struct alias *ap;
11920 ap = lookupalias(wordtext, 1);
11921 if (ap != NULL) {
11922 if (*ap->val) {
11923 pushstring(ap->val, ap);
11924 }
11925 goto top;
11926 }
11927#endif
11928 }
11929 out:
11930 checkkwd = 0;
11931#if DEBUG
11932 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011933 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011934 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011935 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011936#endif
11937 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011938}
11939
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011940static char
11941peektoken(void)
11942{
11943 int t;
11944
11945 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011946 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011947 return tokname_array[t][0];
11948}
Eric Andersencb57d552001-06-28 07:25:16 +000011949
11950/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011951 * Read and parse a command. Returns NODE_EOF on end of file.
11952 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011953 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011954static union node *
11955parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011956{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011957 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011958
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011959 tokpushback = 0;
11960 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011961 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011962 needprompt = 0;
11963 t = readtoken();
11964 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011965 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011966 if (t == TNL)
11967 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011968 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011969 return list(1);
11970}
11971
11972/*
11973 * Input any here documents.
11974 */
11975static void
11976parseheredoc(void)
11977{
11978 struct heredoc *here;
11979 union node *n;
11980
11981 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011982 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011983
11984 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011985 setprompt_if(needprompt, 2);
11986 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011987 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011988 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011989 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011990 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011991 n->narg.text = wordtext;
11992 n->narg.backquote = backquotelist;
11993 here->here->nhere.doc = n;
11994 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011995 }
Eric Andersencb57d552001-06-28 07:25:16 +000011996}
11997
11998
11999/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012000 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012001 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012002#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012003static const char *
12004expandstr(const char *ps)
12005{
12006 union node n;
12007
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012008 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12009 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012010 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012011 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012012 popfile();
12013
12014 n.narg.type = NARG;
12015 n.narg.next = NULL;
12016 n.narg.text = wordtext;
12017 n.narg.backquote = backquotelist;
12018
12019 expandarg(&n, NULL, 0);
12020 return stackblock();
12021}
12022#endif
12023
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012024/*
12025 * Execute a command or commands contained in a string.
12026 */
12027static int
12028evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012029{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012030 union node *n;
12031 struct stackmark smark;
12032 int skip;
12033
12034 setinputstring(s);
12035 setstackmark(&smark);
12036
12037 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012038 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012039 evaltree(n, 0);
12040 popstackmark(&smark);
12041 skip = evalskip;
12042 if (skip)
12043 break;
12044 }
12045 popfile();
12046
12047 skip &= mask;
12048 evalskip = skip;
12049 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012050}
12051
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012052/*
12053 * The eval command.
12054 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012055static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012056evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012057{
12058 char *p;
12059 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012060
Denis Vlasenko68404f12008-03-17 09:00:54 +000012061 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012062 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012063 argv += 2;
12064 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012065 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012066 for (;;) {
12067 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012068 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012069 if (p == NULL)
12070 break;
12071 STPUTC(' ', concat);
12072 }
12073 STPUTC('\0', concat);
12074 p = grabstackstr(concat);
12075 }
12076 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012077 }
12078 return exitstatus;
12079}
12080
12081/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012082 * Read and execute commands.
12083 * "Top" is nonzero for the top level command loop;
12084 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012085 */
12086static int
12087cmdloop(int top)
12088{
12089 union node *n;
12090 struct stackmark smark;
12091 int inter;
12092 int numeof = 0;
12093
12094 TRACE(("cmdloop(%d) called\n", top));
12095 for (;;) {
12096 int skip;
12097
12098 setstackmark(&smark);
12099#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012100 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012101 showjobs(stderr, SHOW_CHANGED);
12102#endif
12103 inter = 0;
12104 if (iflag && top) {
12105 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012106 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012107 }
12108 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012109#if DEBUG
12110 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012111 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012112#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012113 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012114 if (!top || numeof >= 50)
12115 break;
12116 if (!stoppedjobs()) {
12117 if (!Iflag)
12118 break;
12119 out2str("\nUse \"exit\" to leave shell.\n");
12120 }
12121 numeof++;
12122 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012123 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12124 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012125 numeof = 0;
12126 evaltree(n, 0);
12127 }
12128 popstackmark(&smark);
12129 skip = evalskip;
12130
12131 if (skip) {
12132 evalskip = 0;
12133 return skip & SKIPEVAL;
12134 }
12135 }
12136 return 0;
12137}
12138
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012139/*
12140 * Take commands from a file. To be compatible we should do a path
12141 * search for the file, which is necessary to find sub-commands.
12142 */
12143static char *
12144find_dot_file(char *name)
12145{
12146 char *fullname;
12147 const char *path = pathval();
12148 struct stat statb;
12149
12150 /* don't try this for absolute or relative paths */
12151 if (strchr(name, '/'))
12152 return name;
12153
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012154 /* IIRC standards do not say whether . is to be searched.
12155 * And it is even smaller this way, making it unconditional for now:
12156 */
12157 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12158 fullname = name;
12159 goto try_cur_dir;
12160 }
12161
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012162 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012163 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012164 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12165 /*
12166 * Don't bother freeing here, since it will
12167 * be freed by the caller.
12168 */
12169 return fullname;
12170 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012171 if (fullname != name)
12172 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012173 }
12174
12175 /* not found in the PATH */
12176 ash_msg_and_raise_error("%s: not found", name);
12177 /* NOTREACHED */
12178}
12179
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012180static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012181dotcmd(int argc, char **argv)
12182{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012183 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012184 struct strlist *sp;
12185 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012186
12187 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012188 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012189
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012190 if (!argv[1]) {
12191 /* bash says: "bash: .: filename argument required" */
12192 return 2; /* bash compat */
12193 }
12194
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012195 /* "false; . empty_file; echo $?" should print 0, not 1: */
12196 exitstatus = 0;
12197
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012198 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012199
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012200 argv += 2;
12201 argc -= 2;
12202 if (argc) { /* argc > 0, argv[0] != NULL */
12203 saveparam = shellparam;
12204 shellparam.malloced = 0;
12205 shellparam.nparam = argc;
12206 shellparam.p = argv;
12207 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012208
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012209 setinputfile(fullname, INPUT_PUSH_FILE);
12210 commandname = fullname;
12211 cmdloop(0);
12212 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012213
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012214 if (argc) {
12215 freeparam(&shellparam);
12216 shellparam = saveparam;
12217 };
12218
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012219 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012220}
12221
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012222static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012223exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012224{
12225 if (stoppedjobs())
12226 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012227 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012228 exitstatus = number(argv[1]);
12229 raise_exception(EXEXIT);
12230 /* NOTREACHED */
12231}
12232
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012233/*
12234 * Read a file containing shell functions.
12235 */
12236static void
12237readcmdfile(char *name)
12238{
12239 setinputfile(name, INPUT_PUSH_FILE);
12240 cmdloop(0);
12241 popfile();
12242}
12243
12244
Denis Vlasenkocc571512007-02-23 21:10:35 +000012245/* ============ find_command inplementation */
12246
12247/*
12248 * Resolve a command name. If you change this routine, you may have to
12249 * change the shellexec routine as well.
12250 */
12251static void
12252find_command(char *name, struct cmdentry *entry, int act, const char *path)
12253{
12254 struct tblentry *cmdp;
12255 int idx;
12256 int prev;
12257 char *fullname;
12258 struct stat statb;
12259 int e;
12260 int updatetbl;
12261 struct builtincmd *bcmd;
12262
12263 /* If name contains a slash, don't use PATH or hash table */
12264 if (strchr(name, '/') != NULL) {
12265 entry->u.index = -1;
12266 if (act & DO_ABS) {
12267 while (stat(name, &statb) < 0) {
12268#ifdef SYSV
12269 if (errno == EINTR)
12270 continue;
12271#endif
12272 entry->cmdtype = CMDUNKNOWN;
12273 return;
12274 }
12275 }
12276 entry->cmdtype = CMDNORMAL;
12277 return;
12278 }
12279
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012280/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012281
12282 updatetbl = (path == pathval());
12283 if (!updatetbl) {
12284 act |= DO_ALTPATH;
12285 if (strstr(path, "%builtin") != NULL)
12286 act |= DO_ALTBLTIN;
12287 }
12288
12289 /* If name is in the table, check answer will be ok */
12290 cmdp = cmdlookup(name, 0);
12291 if (cmdp != NULL) {
12292 int bit;
12293
12294 switch (cmdp->cmdtype) {
12295 default:
12296#if DEBUG
12297 abort();
12298#endif
12299 case CMDNORMAL:
12300 bit = DO_ALTPATH;
12301 break;
12302 case CMDFUNCTION:
12303 bit = DO_NOFUNC;
12304 break;
12305 case CMDBUILTIN:
12306 bit = DO_ALTBLTIN;
12307 break;
12308 }
12309 if (act & bit) {
12310 updatetbl = 0;
12311 cmdp = NULL;
12312 } else if (cmdp->rehash == 0)
12313 /* if not invalidated by cd, we're done */
12314 goto success;
12315 }
12316
12317 /* If %builtin not in path, check for builtin next */
12318 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012319 if (bcmd) {
12320 if (IS_BUILTIN_REGULAR(bcmd))
12321 goto builtin_success;
12322 if (act & DO_ALTPATH) {
12323 if (!(act & DO_ALTBLTIN))
12324 goto builtin_success;
12325 } else if (builtinloc <= 0) {
12326 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012327 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012328 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012329
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012330#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012331 {
12332 int applet_no = find_applet_by_name(name);
12333 if (applet_no >= 0) {
12334 entry->cmdtype = CMDNORMAL;
12335 entry->u.index = -2 - applet_no;
12336 return;
12337 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012338 }
12339#endif
12340
Denis Vlasenkocc571512007-02-23 21:10:35 +000012341 /* We have to search path. */
12342 prev = -1; /* where to start */
12343 if (cmdp && cmdp->rehash) { /* doing a rehash */
12344 if (cmdp->cmdtype == CMDBUILTIN)
12345 prev = builtinloc;
12346 else
12347 prev = cmdp->param.index;
12348 }
12349
12350 e = ENOENT;
12351 idx = -1;
12352 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012353 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012354 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012355 /* NB: code below will still use fullname
12356 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012357 idx++;
12358 if (pathopt) {
12359 if (prefix(pathopt, "builtin")) {
12360 if (bcmd)
12361 goto builtin_success;
12362 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012363 }
12364 if ((act & DO_NOFUNC)
12365 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012366 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012367 continue;
12368 }
12369 }
12370 /* if rehash, don't redo absolute path names */
12371 if (fullname[0] == '/' && idx <= prev) {
12372 if (idx < prev)
12373 continue;
12374 TRACE(("searchexec \"%s\": no change\n", name));
12375 goto success;
12376 }
12377 while (stat(fullname, &statb) < 0) {
12378#ifdef SYSV
12379 if (errno == EINTR)
12380 continue;
12381#endif
12382 if (errno != ENOENT && errno != ENOTDIR)
12383 e = errno;
12384 goto loop;
12385 }
12386 e = EACCES; /* if we fail, this will be the error */
12387 if (!S_ISREG(statb.st_mode))
12388 continue;
12389 if (pathopt) { /* this is a %func directory */
12390 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012391 /* NB: stalloc will return space pointed by fullname
12392 * (because we don't have any intervening allocations
12393 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012394 readcmdfile(fullname);
12395 cmdp = cmdlookup(name, 0);
12396 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12397 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12398 stunalloc(fullname);
12399 goto success;
12400 }
12401 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12402 if (!updatetbl) {
12403 entry->cmdtype = CMDNORMAL;
12404 entry->u.index = idx;
12405 return;
12406 }
12407 INT_OFF;
12408 cmdp = cmdlookup(name, 1);
12409 cmdp->cmdtype = CMDNORMAL;
12410 cmdp->param.index = idx;
12411 INT_ON;
12412 goto success;
12413 }
12414
12415 /* We failed. If there was an entry for this command, delete it */
12416 if (cmdp && updatetbl)
12417 delete_cmd_entry();
12418 if (act & DO_ERR)
12419 ash_msg("%s: %s", name, errmsg(e, "not found"));
12420 entry->cmdtype = CMDUNKNOWN;
12421 return;
12422
12423 builtin_success:
12424 if (!updatetbl) {
12425 entry->cmdtype = CMDBUILTIN;
12426 entry->u.cmd = bcmd;
12427 return;
12428 }
12429 INT_OFF;
12430 cmdp = cmdlookup(name, 1);
12431 cmdp->cmdtype = CMDBUILTIN;
12432 cmdp->param.cmd = bcmd;
12433 INT_ON;
12434 success:
12435 cmdp->rehash = 0;
12436 entry->cmdtype = cmdp->cmdtype;
12437 entry->u = cmdp->param;
12438}
12439
12440
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012441/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012442
Eric Andersencb57d552001-06-28 07:25:16 +000012443/*
Eric Andersencb57d552001-06-28 07:25:16 +000012444 * The trap builtin.
12445 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012446static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012447trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012448{
12449 char *action;
12450 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012451 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012452
Eric Andersenc470f442003-07-28 09:56:35 +000012453 nextopt(nullstr);
12454 ap = argptr;
12455 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012456 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012457 char *tr = trap_ptr[signo];
12458 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012459 /* note: bash adds "SIG", but only if invoked
12460 * as "bash". If called as "sh", or if set -o posix,
12461 * then it prints short signal names.
12462 * We are printing short names: */
12463 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012464 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012465 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012466 /* trap_ptr != trap only if we are in special-cased `trap` code.
12467 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012468 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012469 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012470 }
12471 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012472 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012473 if (trap_ptr != trap) {
12474 free(trap_ptr);
12475 trap_ptr = trap;
12476 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012477 */
Eric Andersencb57d552001-06-28 07:25:16 +000012478 return 0;
12479 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012480
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012481 action = NULL;
12482 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012483 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012484 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012485 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012486 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012487 if (signo < 0) {
12488 /* Mimic bash message exactly */
12489 ash_msg("%s: invalid signal specification", *ap);
12490 exitcode = 1;
12491 goto next;
12492 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012493 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012494 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012495 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012496 action = NULL;
12497 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012498 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012499 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012500 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012501 if (action)
12502 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012503 trap[signo] = action;
12504 if (signo != 0)
12505 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012506 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012507 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012508 ap++;
12509 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012510 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012511}
12512
Eric Andersenc470f442003-07-28 09:56:35 +000012513
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012514/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012515
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012516#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012517/*
12518 * Lists available builtins
12519 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012520static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012521helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012522{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012523 unsigned col;
12524 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012525
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012526 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012527 "Built-in commands:\n"
12528 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012529 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012530 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012531 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012532 if (col > 60) {
12533 out1fmt("\n");
12534 col = 0;
12535 }
12536 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012537#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012538 {
12539 const char *a = applet_names;
12540 while (*a) {
12541 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12542 if (col > 60) {
12543 out1fmt("\n");
12544 col = 0;
12545 }
12546 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012547 }
12548 }
12549#endif
12550 out1fmt("\n\n");
12551 return EXIT_SUCCESS;
12552}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012553#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012554
Eric Andersencb57d552001-06-28 07:25:16 +000012555/*
Eric Andersencb57d552001-06-28 07:25:16 +000012556 * The export and readonly commands.
12557 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012558static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012559exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012560{
12561 struct var *vp;
12562 char *name;
12563 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012564 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012565 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012566
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012567 if (nextopt("p") != 'p') {
12568 aptr = argptr;
12569 name = *aptr;
12570 if (name) {
12571 do {
12572 p = strchr(name, '=');
12573 if (p != NULL) {
12574 p++;
12575 } else {
12576 vp = *findvar(hashvar(name), name);
12577 if (vp) {
12578 vp->flags |= flag;
12579 continue;
12580 }
Eric Andersencb57d552001-06-28 07:25:16 +000012581 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012582 setvar(name, p, flag);
12583 } while ((name = *++aptr) != NULL);
12584 return 0;
12585 }
Eric Andersencb57d552001-06-28 07:25:16 +000012586 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012587 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012588 return 0;
12589}
12590
Eric Andersencb57d552001-06-28 07:25:16 +000012591/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012592 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012593 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012594static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012595unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012596{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012597 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012598
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012599 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012600 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012601 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012602}
12603
Eric Andersencb57d552001-06-28 07:25:16 +000012604/*
Eric Andersencb57d552001-06-28 07:25:16 +000012605 * The unset builtin command. We unset the function before we unset the
12606 * variable to allow a function to be unset when there is a readonly variable
12607 * with the same name.
12608 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012609static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012610unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012611{
12612 char **ap;
12613 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012614 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012615 int ret = 0;
12616
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012617 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012618 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012619 }
Eric Andersencb57d552001-06-28 07:25:16 +000012620
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012621 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012622 if (flag != 'f') {
12623 i = unsetvar(*ap);
12624 ret |= i;
12625 if (!(i & 2))
12626 continue;
12627 }
12628 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012629 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012630 }
Eric Andersenc470f442003-07-28 09:56:35 +000012631 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012632}
12633
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012634static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012635 ' ', offsetof(struct tms, tms_utime),
12636 '\n', offsetof(struct tms, tms_stime),
12637 ' ', offsetof(struct tms, tms_cutime),
12638 '\n', offsetof(struct tms, tms_cstime),
12639 0
12640};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012641static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012642timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012643{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012644 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012645 const unsigned char *p;
12646 struct tms buf;
12647
12648 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012649 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012650
12651 p = timescmd_str;
12652 do {
12653 t = *(clock_t *)(((char *) &buf) + p[1]);
12654 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012655 t = t % clk_tck;
12656 out1fmt("%lum%lu.%03lus%c",
12657 s / 60, s % 60,
12658 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012659 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012660 p += 2;
12661 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012662
Eric Andersencb57d552001-06-28 07:25:16 +000012663 return 0;
12664}
12665
Mike Frysinger98c52642009-04-02 10:02:37 +000012666#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012667/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012668 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012669 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012670 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012671 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012672 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012673static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012674letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012675{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012676 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012677
Denis Vlasenko68404f12008-03-17 09:00:54 +000012678 argv++;
12679 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012680 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012681 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012682 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012683 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012684
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012685 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012686}
Eric Andersenc470f442003-07-28 09:56:35 +000012687#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012688
Eric Andersenc470f442003-07-28 09:56:35 +000012689/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012690 * The read builtin. Options:
12691 * -r Do not interpret '\' specially
12692 * -s Turn off echo (tty only)
12693 * -n NCHARS Read NCHARS max
12694 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12695 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12696 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012697 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012698 * TODO: bash also has:
12699 * -a ARRAY Read into array[0],[1],etc
12700 * -d DELIM End on DELIM char, not newline
12701 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012702 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012703static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012704readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012705{
Denys Vlasenko73067272010-01-12 22:11:24 +010012706 char *opt_n = NULL;
12707 char *opt_p = NULL;
12708 char *opt_t = NULL;
12709 char *opt_u = NULL;
12710 int read_flags = 0;
12711 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012712 int i;
12713
Denys Vlasenko73067272010-01-12 22:11:24 +010012714 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012715 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012716 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012717 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012718 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012719 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012720 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012721 break;
12722 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012723 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012724 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012725 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012726 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012727 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012728 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012729 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012730 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012731 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012732 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012733 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012734 default:
12735 break;
12736 }
Eric Andersenc470f442003-07-28 09:56:35 +000012737 }
Paul Fox02eb9342005-09-07 16:56:02 +000012738
Denys Vlasenko03dad222010-01-12 23:29:57 +010012739 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012740 argptr,
12741 bltinlookup("IFS"), /* can be NULL */
12742 read_flags,
12743 opt_n,
12744 opt_p,
12745 opt_t,
12746 opt_u
12747 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012748
Denys Vlasenko73067272010-01-12 22:11:24 +010012749 if ((uintptr_t)r > 1)
12750 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012751
Denys Vlasenko73067272010-01-12 22:11:24 +010012752 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012753}
12754
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012755static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012756umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012757{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012758 static const char permuser[3] ALIGN1 = "ugo";
12759 static const char permmode[3] ALIGN1 = "rwx";
12760 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012761 S_IRUSR, S_IWUSR, S_IXUSR,
12762 S_IRGRP, S_IWGRP, S_IXGRP,
12763 S_IROTH, S_IWOTH, S_IXOTH
12764 };
12765
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012766 /* TODO: use bb_parse_mode() instead */
12767
Eric Andersenc470f442003-07-28 09:56:35 +000012768 char *ap;
12769 mode_t mask;
12770 int i;
12771 int symbolic_mode = 0;
12772
12773 while (nextopt("S") != '\0') {
12774 symbolic_mode = 1;
12775 }
12776
Denis Vlasenkob012b102007-02-19 22:43:01 +000012777 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012778 mask = umask(0);
12779 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012780 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012781
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012782 ap = *argptr;
12783 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012784 if (symbolic_mode) {
12785 char buf[18];
12786 char *p = buf;
12787
12788 for (i = 0; i < 3; i++) {
12789 int j;
12790
12791 *p++ = permuser[i];
12792 *p++ = '=';
12793 for (j = 0; j < 3; j++) {
12794 if ((mask & permmask[3 * i + j]) == 0) {
12795 *p++ = permmode[j];
12796 }
12797 }
12798 *p++ = ',';
12799 }
12800 *--p = 0;
12801 puts(buf);
12802 } else {
12803 out1fmt("%.4o\n", mask);
12804 }
12805 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012806 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012807 mask = 0;
12808 do {
12809 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012810 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012811 mask = (mask << 3) + (*ap - '0');
12812 } while (*++ap != '\0');
12813 umask(mask);
12814 } else {
12815 mask = ~mask & 0777;
12816 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012817 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012818 }
12819 umask(~mask & 0777);
12820 }
12821 }
12822 return 0;
12823}
12824
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012825static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012826ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012827{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012828 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012829}
12830
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012831/* ============ main() and helpers */
12832
12833/*
12834 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012835 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012836static void
12837exitshell(void)
12838{
12839 struct jmploc loc;
12840 char *p;
12841 int status;
12842
12843 status = exitstatus;
12844 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12845 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012846 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012847/* dash bug: it just does _exit(exitstatus) here
12848 * but we have to do setjobctl(0) first!
12849 * (bug is still not fixed in dash-0.5.3 - if you run dash
12850 * under Midnight Commander, on exit from dash MC is backgrounded) */
12851 status = exitstatus;
12852 goto out;
12853 }
12854 exception_handler = &loc;
12855 p = trap[0];
12856 if (p) {
12857 trap[0] = NULL;
12858 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012859 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012860 }
12861 flush_stdout_stderr();
12862 out:
12863 setjobctl(0);
12864 _exit(status);
12865 /* NOTREACHED */
12866}
12867
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012868static void
12869init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012870{
12871 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012872 /* we will never free this */
12873 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012874
12875 /* from trap.c: */
12876 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012877 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12878 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12879 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012880 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012881
12882 /* from var.c: */
12883 {
12884 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012885 const char *p;
12886 struct stat st1, st2;
12887
12888 initvar();
12889 for (envp = environ; envp && *envp; envp++) {
12890 if (strchr(*envp, '=')) {
12891 setvareq(*envp, VEXPORT|VTEXTFIXED);
12892 }
12893 }
12894
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012895 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012896
12897 p = lookupvar("PWD");
12898 if (p)
12899 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12900 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12901 p = '\0';
12902 setpwd(p, 0);
12903 }
12904}
12905
12906/*
12907 * Process the shell command line arguments.
12908 */
12909static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012910procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012911{
12912 int i;
12913 const char *xminusc;
12914 char **xargv;
12915
12916 xargv = argv;
12917 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012918 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012919 xargv++;
12920 for (i = 0; i < NOPTS; i++)
12921 optlist[i] = 2;
12922 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012923 if (options(1)) {
12924 /* it already printed err message */
12925 raise_exception(EXERROR);
12926 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012927 xargv = argptr;
12928 xminusc = minusc;
12929 if (*xargv == NULL) {
12930 if (xminusc)
12931 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12932 sflag = 1;
12933 }
12934 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12935 iflag = 1;
12936 if (mflag == 2)
12937 mflag = iflag;
12938 for (i = 0; i < NOPTS; i++)
12939 if (optlist[i] == 2)
12940 optlist[i] = 0;
12941#if DEBUG == 2
12942 debug = 1;
12943#endif
12944 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12945 if (xminusc) {
12946 minusc = *xargv++;
12947 if (*xargv)
12948 goto setarg0;
12949 } else if (!sflag) {
12950 setinputfile(*xargv, 0);
12951 setarg0:
12952 arg0 = *xargv++;
12953 commandname = arg0;
12954 }
12955
12956 shellparam.p = xargv;
12957#if ENABLE_ASH_GETOPTS
12958 shellparam.optind = 1;
12959 shellparam.optoff = -1;
12960#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012961 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012962 while (*xargv) {
12963 shellparam.nparam++;
12964 xargv++;
12965 }
12966 optschanged();
12967}
12968
12969/*
12970 * Read /etc/profile or .profile.
12971 */
12972static void
12973read_profile(const char *name)
12974{
12975 int skip;
12976
12977 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12978 return;
12979 skip = cmdloop(0);
12980 popfile();
12981 if (skip)
12982 exitshell();
12983}
12984
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012985/*
12986 * This routine is called when an error or an interrupt occurs in an
12987 * interactive shell and control is returned to the main command loop.
12988 */
12989static void
12990reset(void)
12991{
12992 /* from eval.c: */
12993 evalskip = 0;
12994 loopnest = 0;
12995 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012996 g_parsefile->left_in_buffer = 0;
12997 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012998 popallfiles();
12999 /* from parser.c: */
13000 tokpushback = 0;
13001 checkkwd = 0;
13002 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013003 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013004}
13005
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006#if PROFILE
13007static short profile_buf[16384];
13008extern int etext();
13009#endif
13010
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013011/*
13012 * Main routine. We initialize things, parse the arguments, execute
13013 * profiles if we're a login shell, and then call cmdloop to execute
13014 * commands. The setjmp call sets up the location to jump to when an
13015 * exception occurs. When an exception occurs the variable "state"
13016 * is used to figure out how far we had gotten.
13017 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013018int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013019int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013020{
Mike Frysinger98c52642009-04-02 10:02:37 +000013021 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013022 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023 struct jmploc jmploc;
13024 struct stackmark smark;
13025
Denis Vlasenko01631112007-12-16 17:20:38 +000013026 /* Initialize global data */
13027 INIT_G_misc();
13028 INIT_G_memstack();
13029 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013030#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013031 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013032#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013033 INIT_G_cmdtable();
13034
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013035#if PROFILE
13036 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13037#endif
13038
13039#if ENABLE_FEATURE_EDITING
13040 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13041#endif
13042 state = 0;
13043 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013044 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013045 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013046
13047 reset();
13048
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013049 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013050 if (e == EXERROR)
13051 exitstatus = 2;
13052 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013053 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013054 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013055 }
13056 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013057 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013058 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013059
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013060 popstackmark(&smark);
13061 FORCE_INT_ON; /* enable interrupts */
13062 if (s == 1)
13063 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013064 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013065 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013066 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013067 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013068 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013069 }
13070 exception_handler = &jmploc;
13071#if DEBUG
13072 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013073 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013074 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013075#endif
13076 rootpid = getpid();
13077
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013078 init();
13079 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013080 procargs(argv);
13081
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013082#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13083 if (iflag) {
13084 const char *hp = lookupvar("HISTFILE");
13085
13086 if (hp == NULL) {
13087 hp = lookupvar("HOME");
13088 if (hp != NULL) {
13089 char *defhp = concat_path_file(hp, ".ash_history");
13090 setvar("HISTFILE", defhp, 0);
13091 free(defhp);
13092 }
13093 }
13094 }
13095#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013096 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013097 isloginsh = 1;
13098 if (isloginsh) {
13099 state = 1;
13100 read_profile("/etc/profile");
13101 state1:
13102 state = 2;
13103 read_profile(".profile");
13104 }
13105 state2:
13106 state = 3;
13107 if (
13108#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013109 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013110#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013111 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013112 ) {
13113 shinit = lookupvar("ENV");
13114 if (shinit != NULL && *shinit != '\0') {
13115 read_profile(shinit);
13116 }
13117 }
13118 state3:
13119 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013120 if (minusc) {
13121 /* evalstring pushes parsefile stack.
13122 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013123 * is one of stacked source fds.
13124 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013125 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013126 // ^^ not necessary since now we special-case fd 0
13127 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013128 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013129 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013130
13131 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013132#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013133 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013134 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013135 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013136 line_input_state->hist_file = hp;
13137 }
13138#endif
13139 state4: /* XXX ??? - why isn't this before the "if" statement */
13140 cmdloop(1);
13141 }
13142#if PROFILE
13143 monitor(0);
13144#endif
13145#ifdef GPROF
13146 {
13147 extern void _mcleanup(void);
13148 _mcleanup();
13149 }
13150#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013151 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013152 exitshell();
13153 /* NOTREACHED */
13154}
13155
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013156
Eric Andersendf82f612001-06-28 07:46:40 +000013157/*-
13158 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013159 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013160 *
13161 * This code is derived from software contributed to Berkeley by
13162 * Kenneth Almquist.
13163 *
13164 * Redistribution and use in source and binary forms, with or without
13165 * modification, are permitted provided that the following conditions
13166 * are met:
13167 * 1. Redistributions of source code must retain the above copyright
13168 * notice, this list of conditions and the following disclaimer.
13169 * 2. Redistributions in binary form must reproduce the above copyright
13170 * notice, this list of conditions and the following disclaimer in the
13171 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013172 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013173 * may be used to endorse or promote products derived from this software
13174 * without specific prior written permission.
13175 *
13176 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13177 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13178 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13179 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13180 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13181 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13182 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13183 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13184 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13185 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13186 * SUCH DAMAGE.
13187 */