blob: 98d2c7c29edb47728ee653e86027b39c1a1f03a9 [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 Vlasenkof20de5b2007-04-29 23:42:54 +00003786 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003787 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003788 do {
3789 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003790 /*
3791 * "kill %N" - job kill
3792 * Converting to pgrp / pid kill
3793 */
3794 struct job *jp;
3795 char *dst;
3796 int j, n;
3797
3798 jp = getjob(argv[i], 0);
3799 /*
3800 * In jobs started under job control, we signal
3801 * entire process group by kill -PGRP_ID.
3802 * This happens, f.e., in interactive shell.
3803 *
3804 * Otherwise, we signal each child via
3805 * kill PID1 PID2 PID3.
3806 * Testcases:
3807 * sh -c 'sleep 1|sleep 1 & kill %1'
3808 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3809 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3810 */
3811 n = jp->nprocs; /* can't be 0 (I hope) */
3812 if (jp->jobctl)
3813 n = 1;
3814 dst = alloca(n * sizeof(int)*4);
3815 argv[i] = dst;
3816 for (j = 0; j < n; j++) {
3817 struct procstat *ps = &jp->ps[j];
3818 /* Skip non-running and not-stopped members
3819 * (i.e. dead members) of the job
3820 */
3821 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3822 continue;
3823 /*
3824 * kill_main has matching code to expect
3825 * leading space. Needed to not confuse
3826 * negative pids with "kill -SIGNAL_NO" syntax
3827 */
3828 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3829 }
3830 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003831 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003832 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003833 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003834 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835}
3836
3837static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003838showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003840 struct procstat *ps;
3841 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842
Denys Vlasenko285ad152009-12-04 23:02:27 +01003843 psend = jp->ps + jp->nprocs;
3844 for (ps = jp->ps + 1; ps < psend; ps++)
3845 printf(" | %s", ps->ps_cmd);
3846 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003847 flush_stdout_stderr();
3848}
3849
3850
3851static int
3852restartjob(struct job *jp, int mode)
3853{
3854 struct procstat *ps;
3855 int i;
3856 int status;
3857 pid_t pgid;
3858
3859 INT_OFF;
3860 if (jp->state == JOBDONE)
3861 goto out;
3862 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003863 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003864 if (mode == FORK_FG)
3865 xtcsetpgrp(ttyfd, pgid);
3866 killpg(pgid, SIGCONT);
3867 ps = jp->ps;
3868 i = jp->nprocs;
3869 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003870 if (WIFSTOPPED(ps->ps_status)) {
3871 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003872 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003873 ps++;
3874 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003875 out:
3876 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3877 INT_ON;
3878 return status;
3879}
3880
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003881static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003882fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883{
3884 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003885 int mode;
3886 int retval;
3887
3888 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3889 nextopt(nullstr);
3890 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003891 do {
3892 jp = getjob(*argv, 1);
3893 if (mode == FORK_BG) {
3894 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003895 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003897 out1str(jp->ps[0].ps_cmd);
3898 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003899 retval = restartjob(jp, mode);
3900 } while (*argv && *++argv);
3901 return retval;
3902}
3903#endif
3904
3905static int
3906sprint_status(char *s, int status, int sigonly)
3907{
3908 int col;
3909 int st;
3910
3911 col = 0;
3912 if (!WIFEXITED(status)) {
3913#if JOBS
3914 if (WIFSTOPPED(status))
3915 st = WSTOPSIG(status);
3916 else
3917#endif
3918 st = WTERMSIG(status);
3919 if (sigonly) {
3920 if (st == SIGINT || st == SIGPIPE)
3921 goto out;
3922#if JOBS
3923 if (WIFSTOPPED(status))
3924 goto out;
3925#endif
3926 }
3927 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003928//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003929 col = fmtstr(s, 32, strsignal(st));
3930 if (WCOREDUMP(status)) {
3931 col += fmtstr(s + col, 16, " (core dumped)");
3932 }
3933 } else if (!sigonly) {
3934 st = WEXITSTATUS(status);
3935 if (st)
3936 col = fmtstr(s, 16, "Done(%d)", st);
3937 else
3938 col = fmtstr(s, 16, "Done");
3939 }
3940 out:
3941 return col;
3942}
3943
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003944static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003945dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003946{
3947 int pid;
3948 int status;
3949 struct job *jp;
3950 struct job *thisjob;
3951 int state;
3952
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003953 TRACE(("dowait(0x%x) called\n", wait_flags));
3954
3955 /* Do a wait system call. If job control is compiled in, we accept
3956 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3957 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003958 if (doing_jobctl)
3959 wait_flags |= WUNTRACED;
3960 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003961 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3962 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003963 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003965
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 INT_OFF;
3967 thisjob = NULL;
3968 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003969 struct procstat *ps;
3970 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 if (jp->state == JOBDONE)
3972 continue;
3973 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003974 ps = jp->ps;
3975 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003977 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003978 TRACE(("Job %d: changing status of proc %d "
3979 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003980 jobno(jp), pid, ps->ps_status, status));
3981 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003982 thisjob = jp;
3983 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003984 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003985 state = JOBRUNNING;
3986#if JOBS
3987 if (state == JOBRUNNING)
3988 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003989 if (WIFSTOPPED(ps->ps_status)) {
3990 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003991 state = JOBSTOPPED;
3992 }
3993#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003994 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003995 if (thisjob)
3996 goto gotjob;
3997 }
3998#if JOBS
3999 if (!WIFSTOPPED(status))
4000#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004001 jobless--;
4002 goto out;
4003
4004 gotjob:
4005 if (state != JOBRUNNING) {
4006 thisjob->changed = 1;
4007
4008 if (thisjob->state != state) {
4009 TRACE(("Job %d: changing state from %d to %d\n",
4010 jobno(thisjob), thisjob->state, state));
4011 thisjob->state = state;
4012#if JOBS
4013 if (state == JOBSTOPPED) {
4014 set_curjob(thisjob, CUR_STOPPED);
4015 }
4016#endif
4017 }
4018 }
4019
4020 out:
4021 INT_ON;
4022
4023 if (thisjob && thisjob == job) {
4024 char s[48 + 1];
4025 int len;
4026
4027 len = sprint_status(s, status, 1);
4028 if (len) {
4029 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004030 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004031 out2str(s);
4032 }
4033 }
4034 return pid;
4035}
4036
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004037static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004038blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004039{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004040 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004041 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004042 raise_exception(EXSIG);
4043 return pid;
4044}
4045
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046#if JOBS
4047static void
4048showjob(FILE *out, struct job *jp, int mode)
4049{
4050 struct procstat *ps;
4051 struct procstat *psend;
4052 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004053 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 char s[80];
4055
4056 ps = jp->ps;
4057
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004058 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004060 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004061 return;
4062 }
4063
4064 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004065 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066
4067 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004068 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004069 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004070 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004072 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004073 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074
4075 psend = ps + jp->nprocs;
4076
4077 if (jp->state == JOBRUNNING) {
4078 strcpy(s + col, "Running");
4079 col += sizeof("Running") - 1;
4080 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004081 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082 if (jp->state == JOBSTOPPED)
4083 status = jp->stopstatus;
4084 col += sprint_status(s + col, status, 0);
4085 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004086 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004088 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4089 * or prints several "PID | <cmdN>" lines,
4090 * depending on SHOW_PIDS bit.
4091 * We do not print status of individual processes
4092 * between PID and <cmdN>. bash does it, but not very well:
4093 * first line shows overall job status, not process status,
4094 * making it impossible to know 1st process status.
4095 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004097 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004098 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004099 s[0] = '\0';
4100 col = 33;
4101 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004102 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004103 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004104 fprintf(out, "%s%*c%s%s",
4105 s,
4106 33 - col >= 0 ? 33 - col : 0, ' ',
4107 ps == jp->ps ? "" : "| ",
4108 ps->ps_cmd
4109 );
4110 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004111 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004112
4113 jp->changed = 0;
4114
4115 if (jp->state == JOBDONE) {
4116 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4117 freejob(jp);
4118 }
4119}
4120
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121/*
4122 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4123 * statuses have changed since the last call to showjobs.
4124 */
4125static void
4126showjobs(FILE *out, int mode)
4127{
4128 struct job *jp;
4129
Denys Vlasenko883cea42009-07-11 15:31:59 +02004130 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004131
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004132 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004133 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 continue;
4135
4136 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004137 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004139 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004140 }
4141}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004142
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004143static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004144jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004145{
4146 int mode, m;
4147
4148 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004149 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004150 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004151 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004152 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004153 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004154 }
4155
4156 argv = argptr;
4157 if (*argv) {
4158 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004159 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004160 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004161 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004162 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004163 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004164
4165 return 0;
4166}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004167#endif /* JOBS */
4168
Michael Abbott359da5e2009-12-04 23:03:29 +01004169/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170static int
4171getstatus(struct job *job)
4172{
4173 int status;
4174 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004175 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176
Michael Abbott359da5e2009-12-04 23:03:29 +01004177 /* Fetch last member's status */
4178 ps = job->ps + job->nprocs - 1;
4179 status = ps->ps_status;
4180 if (pipefail) {
4181 /* "set -o pipefail" mode: use last _nonzero_ status */
4182 while (status == 0 && --ps >= job->ps)
4183 status = ps->ps_status;
4184 }
4185
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004186 retval = WEXITSTATUS(status);
4187 if (!WIFEXITED(status)) {
4188#if JOBS
4189 retval = WSTOPSIG(status);
4190 if (!WIFSTOPPED(status))
4191#endif
4192 {
4193 /* XXX: limits number of signals */
4194 retval = WTERMSIG(status);
4195#if JOBS
4196 if (retval == SIGINT)
4197 job->sigint = 1;
4198#endif
4199 }
4200 retval += 128;
4201 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004202 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 jobno(job), job->nprocs, status, retval));
4204 return retval;
4205}
4206
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004207static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004208waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004209{
4210 struct job *job;
4211 int retval;
4212 struct job *jp;
4213
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004214 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004215 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216
4217 nextopt(nullstr);
4218 retval = 0;
4219
4220 argv = argptr;
4221 if (!*argv) {
4222 /* wait for all jobs */
4223 for (;;) {
4224 jp = curjob;
4225 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004226 if (!jp) /* no running procs */
4227 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004228 if (jp->state == JOBRUNNING)
4229 break;
4230 jp->waited = 1;
4231 jp = jp->prev_job;
4232 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004233 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004234 /* man bash:
4235 * "When bash is waiting for an asynchronous command via
4236 * the wait builtin, the reception of a signal for which a trap
4237 * has been set will cause the wait builtin to return immediately
4238 * with an exit status greater than 128, immediately after which
4239 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004240 *
4241 * blocking_wait_with_raise_on_sig raises signal handlers
4242 * if it gets no pid (pid < 0). However,
4243 * if child sends us a signal *and immediately exits*,
4244 * blocking_wait_with_raise_on_sig gets pid > 0
4245 * and does not handle pending_sig. Check this case: */
4246 if (pending_sig)
4247 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 }
4249 }
4250
4251 retval = 127;
4252 do {
4253 if (**argv != '%') {
4254 pid_t pid = number(*argv);
4255 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004256 while (1) {
4257 if (!job)
4258 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004259 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004260 break;
4261 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004262 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004263 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004265 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004266 /* loop until process terminated or stopped */
4267 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004268 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004269 job->waited = 1;
4270 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004271 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004272 } while (*++argv);
4273
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004274 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004275 return retval;
4276}
4277
4278static struct job *
4279growjobtab(void)
4280{
4281 size_t len;
4282 ptrdiff_t offset;
4283 struct job *jp, *jq;
4284
4285 len = njobs * sizeof(*jp);
4286 jq = jobtab;
4287 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4288
4289 offset = (char *)jp - (char *)jq;
4290 if (offset) {
4291 /* Relocate pointers */
4292 size_t l = len;
4293
4294 jq = (struct job *)((char *)jq + l);
4295 while (l) {
4296 l -= sizeof(*jp);
4297 jq--;
4298#define joff(p) ((struct job *)((char *)(p) + l))
4299#define jmove(p) (p) = (void *)((char *)(p) + offset)
4300 if (joff(jp)->ps == &jq->ps0)
4301 jmove(joff(jp)->ps);
4302 if (joff(jp)->prev_job)
4303 jmove(joff(jp)->prev_job);
4304 }
4305 if (curjob)
4306 jmove(curjob);
4307#undef joff
4308#undef jmove
4309 }
4310
4311 njobs += 4;
4312 jobtab = jp;
4313 jp = (struct job *)((char *)jp + len);
4314 jq = jp + 3;
4315 do {
4316 jq->used = 0;
4317 } while (--jq >= jp);
4318 return jp;
4319}
4320
4321/*
4322 * Return a new job structure.
4323 * Called with interrupts off.
4324 */
4325static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004326makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004327{
4328 int i;
4329 struct job *jp;
4330
4331 for (i = njobs, jp = jobtab; ; jp++) {
4332 if (--i < 0) {
4333 jp = growjobtab();
4334 break;
4335 }
4336 if (jp->used == 0)
4337 break;
4338 if (jp->state != JOBDONE || !jp->waited)
4339 continue;
4340#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004341 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004342 continue;
4343#endif
4344 freejob(jp);
4345 break;
4346 }
4347 memset(jp, 0, sizeof(*jp));
4348#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004349 /* jp->jobctl is a bitfield.
4350 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004351 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004352 jp->jobctl = 1;
4353#endif
4354 jp->prev_job = curjob;
4355 curjob = jp;
4356 jp->used = 1;
4357 jp->ps = &jp->ps0;
4358 if (nprocs > 1) {
4359 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4360 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004361 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004362 jobno(jp)));
4363 return jp;
4364}
4365
4366#if JOBS
4367/*
4368 * Return a string identifying a command (to be printed by the
4369 * jobs command).
4370 */
4371static char *cmdnextc;
4372
4373static void
4374cmdputs(const char *s)
4375{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004376 static const char vstype[VSTYPE + 1][3] = {
4377 "", "}", "-", "+", "?", "=",
4378 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004379 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004380 };
4381
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004383 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004384 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004385 unsigned char c;
4386 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004388
Denys Vlasenko46a14772009-12-10 21:27:13 +01004389 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004390 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4391 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004392 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004393 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004394 switch (c) {
4395 case CTLESC:
4396 c = *p++;
4397 break;
4398 case CTLVAR:
4399 subtype = *p++;
4400 if ((subtype & VSTYPE) == VSLENGTH)
4401 str = "${#";
4402 else
4403 str = "${";
4404 if (!(subtype & VSQUOTE) == !(quoted & 1))
4405 goto dostr;
4406 quoted ^= 1;
4407 c = '"';
4408 break;
4409 case CTLENDVAR:
4410 str = "\"}" + !(quoted & 1);
4411 quoted >>= 1;
4412 subtype = 0;
4413 goto dostr;
4414 case CTLBACKQ:
4415 str = "$(...)";
4416 goto dostr;
4417 case CTLBACKQ+CTLQUOTE:
4418 str = "\"$(...)\"";
4419 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004420#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004421 case CTLARI:
4422 str = "$((";
4423 goto dostr;
4424 case CTLENDARI:
4425 str = "))";
4426 goto dostr;
4427#endif
4428 case CTLQUOTEMARK:
4429 quoted ^= 1;
4430 c = '"';
4431 break;
4432 case '=':
4433 if (subtype == 0)
4434 break;
4435 if ((subtype & VSTYPE) != VSNORMAL)
4436 quoted <<= 1;
4437 str = vstype[subtype & VSTYPE];
4438 if (subtype & VSNUL)
4439 c = ':';
4440 else
4441 goto checkstr;
4442 break;
4443 case '\'':
4444 case '\\':
4445 case '"':
4446 case '$':
4447 /* These can only happen inside quotes */
4448 cc[0] = c;
4449 str = cc;
4450 c = '\\';
4451 break;
4452 default:
4453 break;
4454 }
4455 USTPUTC(c, nextc);
4456 checkstr:
4457 if (!str)
4458 continue;
4459 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004460 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004461 USTPUTC(c, nextc);
4462 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004463 } /* while *p++ not NUL */
4464
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004465 if (quoted & 1) {
4466 USTPUTC('"', nextc);
4467 }
4468 *nextc = 0;
4469 cmdnextc = nextc;
4470}
4471
4472/* cmdtxt() and cmdlist() call each other */
4473static void cmdtxt(union node *n);
4474
4475static void
4476cmdlist(union node *np, int sep)
4477{
4478 for (; np; np = np->narg.next) {
4479 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004480 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004481 cmdtxt(np);
4482 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004483 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004484 }
4485}
4486
4487static void
4488cmdtxt(union node *n)
4489{
4490 union node *np;
4491 struct nodelist *lp;
4492 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493
4494 if (!n)
4495 return;
4496 switch (n->type) {
4497 default:
4498#if DEBUG
4499 abort();
4500#endif
4501 case NPIPE:
4502 lp = n->npipe.cmdlist;
4503 for (;;) {
4504 cmdtxt(lp->n);
4505 lp = lp->next;
4506 if (!lp)
4507 break;
4508 cmdputs(" | ");
4509 }
4510 break;
4511 case NSEMI:
4512 p = "; ";
4513 goto binop;
4514 case NAND:
4515 p = " && ";
4516 goto binop;
4517 case NOR:
4518 p = " || ";
4519 binop:
4520 cmdtxt(n->nbinary.ch1);
4521 cmdputs(p);
4522 n = n->nbinary.ch2;
4523 goto donode;
4524 case NREDIR:
4525 case NBACKGND:
4526 n = n->nredir.n;
4527 goto donode;
4528 case NNOT:
4529 cmdputs("!");
4530 n = n->nnot.com;
4531 donode:
4532 cmdtxt(n);
4533 break;
4534 case NIF:
4535 cmdputs("if ");
4536 cmdtxt(n->nif.test);
4537 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004539 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004540 cmdputs("; else ");
4541 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004542 } else {
4543 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 }
4545 p = "; fi";
4546 goto dotail;
4547 case NSUBSHELL:
4548 cmdputs("(");
4549 n = n->nredir.n;
4550 p = ")";
4551 goto dotail;
4552 case NWHILE:
4553 p = "while ";
4554 goto until;
4555 case NUNTIL:
4556 p = "until ";
4557 until:
4558 cmdputs(p);
4559 cmdtxt(n->nbinary.ch1);
4560 n = n->nbinary.ch2;
4561 p = "; done";
4562 dodo:
4563 cmdputs("; do ");
4564 dotail:
4565 cmdtxt(n);
4566 goto dotail2;
4567 case NFOR:
4568 cmdputs("for ");
4569 cmdputs(n->nfor.var);
4570 cmdputs(" in ");
4571 cmdlist(n->nfor.args, 1);
4572 n = n->nfor.body;
4573 p = "; done";
4574 goto dodo;
4575 case NDEFUN:
4576 cmdputs(n->narg.text);
4577 p = "() { ... }";
4578 goto dotail2;
4579 case NCMD:
4580 cmdlist(n->ncmd.args, 1);
4581 cmdlist(n->ncmd.redirect, 0);
4582 break;
4583 case NARG:
4584 p = n->narg.text;
4585 dotail2:
4586 cmdputs(p);
4587 break;
4588 case NHERE:
4589 case NXHERE:
4590 p = "<<...";
4591 goto dotail2;
4592 case NCASE:
4593 cmdputs("case ");
4594 cmdputs(n->ncase.expr->narg.text);
4595 cmdputs(" in ");
4596 for (np = n->ncase.cases; np; np = np->nclist.next) {
4597 cmdtxt(np->nclist.pattern);
4598 cmdputs(") ");
4599 cmdtxt(np->nclist.body);
4600 cmdputs(";; ");
4601 }
4602 p = "esac";
4603 goto dotail2;
4604 case NTO:
4605 p = ">";
4606 goto redir;
4607 case NCLOBBER:
4608 p = ">|";
4609 goto redir;
4610 case NAPPEND:
4611 p = ">>";
4612 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004613#if ENABLE_ASH_BASH_COMPAT
4614 case NTO2:
4615#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 case NTOFD:
4617 p = ">&";
4618 goto redir;
4619 case NFROM:
4620 p = "<";
4621 goto redir;
4622 case NFROMFD:
4623 p = "<&";
4624 goto redir;
4625 case NFROMTO:
4626 p = "<>";
4627 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004628 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004629 cmdputs(p);
4630 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004631 cmdputs(utoa(n->ndup.dupfd));
4632 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004633 }
4634 n = n->nfile.fname;
4635 goto donode;
4636 }
4637}
4638
4639static char *
4640commandtext(union node *n)
4641{
4642 char *name;
4643
4644 STARTSTACKSTR(cmdnextc);
4645 cmdtxt(n);
4646 name = stackblock();
4647 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4648 name, cmdnextc, cmdnextc));
4649 return ckstrdup(name);
4650}
4651#endif /* JOBS */
4652
4653/*
4654 * Fork off a subshell. If we are doing job control, give the subshell its
4655 * own process group. Jp is a job structure that the job is to be added to.
4656 * N is the command that will be evaluated by the child. Both jp and n may
4657 * be NULL. The mode parameter can be one of the following:
4658 * FORK_FG - Fork off a foreground process.
4659 * FORK_BG - Fork off a background process.
4660 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4661 * process group even if job control is on.
4662 *
4663 * When job control is turned off, background processes have their standard
4664 * input redirected to /dev/null (except for the second and later processes
4665 * in a pipeline).
4666 *
4667 * Called with interrupts off.
4668 */
4669/*
4670 * Clear traps on a fork.
4671 */
4672static void
4673clear_traps(void)
4674{
4675 char **tp;
4676
4677 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004678 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004679 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004680 if (trap_ptr == trap)
4681 free(*tp);
4682 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004683 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004684 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004685 setsignal(tp - trap);
4686 INT_ON;
4687 }
4688 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004689 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004690}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004691
4692/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004693static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004694
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004695/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004696static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004697forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004698{
4699 int oldlvl;
4700
4701 TRACE(("Child shell %d\n", getpid()));
4702 oldlvl = shlvl;
4703 shlvl++;
4704
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004705 /* man bash: "Non-builtin commands run by bash have signal handlers
4706 * set to the values inherited by the shell from its parent".
4707 * Do we do it correctly? */
4708
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004710
4711 if (mode == FORK_NOJOB /* is it `xxx` ? */
4712 && n && n->type == NCMD /* is it single cmd? */
4713 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004714 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004715 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4716 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4717 ) {
4718 TRACE(("Trap hack\n"));
4719 /* Awful hack for `trap` or $(trap).
4720 *
4721 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4722 * contains an example where "trap" is executed in a subshell:
4723 *
4724 * save_traps=$(trap)
4725 * ...
4726 * eval "$save_traps"
4727 *
4728 * Standard does not say that "trap" in subshell shall print
4729 * parent shell's traps. It only says that its output
4730 * must have suitable form, but then, in the above example
4731 * (which is not supposed to be normative), it implies that.
4732 *
4733 * bash (and probably other shell) does implement it
4734 * (traps are reset to defaults, but "trap" still shows them),
4735 * but as a result, "trap" logic is hopelessly messed up:
4736 *
4737 * # trap
4738 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4739 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4740 * # true | trap <--- trap is in subshell - no output (ditto)
4741 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4742 * trap -- 'echo Ho' SIGWINCH
4743 * # echo `(trap)` <--- in subshell in subshell - output
4744 * trap -- 'echo Ho' SIGWINCH
4745 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4746 * trap -- 'echo Ho' SIGWINCH
4747 *
4748 * The rules when to forget and when to not forget traps
4749 * get really complex and nonsensical.
4750 *
4751 * Our solution: ONLY bare $(trap) or `trap` is special.
4752 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004753 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004754 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004755 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004756 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004757 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004758#if JOBS
4759 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004760 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004761 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004762 pid_t pgrp;
4763
4764 if (jp->nprocs == 0)
4765 pgrp = getpid();
4766 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004767 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004768 /* this can fail because we are doing it in the parent also */
4769 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004770 if (mode == FORK_FG)
4771 xtcsetpgrp(ttyfd, pgrp);
4772 setsignal(SIGTSTP);
4773 setsignal(SIGTTOU);
4774 } else
4775#endif
4776 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004777 /* man bash: "When job control is not in effect,
4778 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004779 ignoresig(SIGINT);
4780 ignoresig(SIGQUIT);
4781 if (jp->nprocs == 0) {
4782 close(0);
4783 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004784 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004785 }
4786 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004787 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004788 if (iflag) { /* why if iflag only? */
4789 setsignal(SIGINT);
4790 setsignal(SIGTERM);
4791 }
4792 /* man bash:
4793 * "In all cases, bash ignores SIGQUIT. Non-builtin
4794 * commands run by bash have signal handlers
4795 * set to the values inherited by the shell
4796 * from its parent".
4797 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004798 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004799 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004800#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004801 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004802 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004803 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004804 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004805 /* "jobs": we do not want to clear job list for it,
4806 * instead we remove only _its_ own_ job from job list.
4807 * This makes "jobs .... | cat" more useful.
4808 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004809 freejob(curjob);
4810 return;
4811 }
4812#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004813 for (jp = curjob; jp; jp = jp->prev_job)
4814 freejob(jp);
4815 jobless = 0;
4816}
4817
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004818/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004819#if !JOBS
4820#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4821#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822static void
4823forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4824{
4825 TRACE(("In parent shell: child = %d\n", pid));
4826 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004827 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4828 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004829 jobless++;
4830 return;
4831 }
4832#if JOBS
4833 if (mode != FORK_NOJOB && jp->jobctl) {
4834 int pgrp;
4835
4836 if (jp->nprocs == 0)
4837 pgrp = pid;
4838 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004839 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004840 /* This can fail because we are doing it in the child also */
4841 setpgid(pid, pgrp);
4842 }
4843#endif
4844 if (mode == FORK_BG) {
4845 backgndpid = pid; /* set $! */
4846 set_curjob(jp, CUR_RUNNING);
4847 }
4848 if (jp) {
4849 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004850 ps->ps_pid = pid;
4851 ps->ps_status = -1;
4852 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004853#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004854 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004855 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004856#endif
4857 }
4858}
4859
4860static int
4861forkshell(struct job *jp, union node *n, int mode)
4862{
4863 int pid;
4864
4865 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4866 pid = fork();
4867 if (pid < 0) {
4868 TRACE(("Fork failed, errno=%d", errno));
4869 if (jp)
4870 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004871 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004872 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004873 if (pid == 0) {
4874 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004875 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004876 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004877 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004878 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004879 return pid;
4880}
4881
4882/*
4883 * Wait for job to finish.
4884 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004885 * Under job control we have the problem that while a child process
4886 * is running interrupts generated by the user are sent to the child
4887 * but not to the shell. This means that an infinite loop started by
4888 * an interactive user may be hard to kill. With job control turned off,
4889 * an interactive user may place an interactive program inside a loop.
4890 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 * these interrupts to also abort the loop. The approach we take here
4892 * is to have the shell ignore interrupt signals while waiting for a
4893 * foreground process to terminate, and then send itself an interrupt
4894 * signal if the child process was terminated by an interrupt signal.
4895 * Unfortunately, some programs want to do a bit of cleanup and then
4896 * exit on interrupt; unless these processes terminate themselves by
4897 * sending a signal to themselves (instead of calling exit) they will
4898 * confuse this approach.
4899 *
4900 * Called with interrupts off.
4901 */
4902static int
4903waitforjob(struct job *jp)
4904{
4905 int st;
4906
4907 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004908
4909 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004910 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004911 /* In non-interactive shells, we _can_ get
4912 * a keyboard signal here and be EINTRed,
4913 * but we just loop back, waiting for command to complete.
4914 *
4915 * man bash:
4916 * "If bash is waiting for a command to complete and receives
4917 * a signal for which a trap has been set, the trap
4918 * will not be executed until the command completes."
4919 *
4920 * Reality is that even if trap is not set, bash
4921 * will not act on the signal until command completes.
4922 * Try this. sleep5intoff.c:
4923 * #include <signal.h>
4924 * #include <unistd.h>
4925 * int main() {
4926 * sigset_t set;
4927 * sigemptyset(&set);
4928 * sigaddset(&set, SIGINT);
4929 * sigaddset(&set, SIGQUIT);
4930 * sigprocmask(SIG_BLOCK, &set, NULL);
4931 * sleep(5);
4932 * return 0;
4933 * }
4934 * $ bash -c './sleep5intoff; echo hi'
4935 * ^C^C^C^C <--- pressing ^C once a second
4936 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004937 * $ bash -c './sleep5intoff; echo hi'
4938 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4939 * $ _
4940 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004941 dowait(DOWAIT_BLOCK, jp);
4942 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004943 INT_ON;
4944
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004945 st = getstatus(jp);
4946#if JOBS
4947 if (jp->jobctl) {
4948 xtcsetpgrp(ttyfd, rootpid);
4949 /*
4950 * This is truly gross.
4951 * If we're doing job control, then we did a TIOCSPGRP which
4952 * caused us (the shell) to no longer be in the controlling
4953 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4954 * intuit from the subprocess exit status whether a SIGINT
4955 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4956 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004957 if (jp->sigint) /* TODO: do the same with all signals */
4958 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004959 }
4960 if (jp->state == JOBDONE)
4961#endif
4962 freejob(jp);
4963 return st;
4964}
4965
4966/*
4967 * return 1 if there are stopped jobs, otherwise 0
4968 */
4969static int
4970stoppedjobs(void)
4971{
4972 struct job *jp;
4973 int retval;
4974
4975 retval = 0;
4976 if (job_warning)
4977 goto out;
4978 jp = curjob;
4979 if (jp && jp->state == JOBSTOPPED) {
4980 out2str("You have stopped jobs.\n");
4981 job_warning = 2;
4982 retval++;
4983 }
4984 out:
4985 return retval;
4986}
4987
4988
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004989/* ============ redir.c
4990 *
4991 * Code for dealing with input/output redirection.
4992 */
4993
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004994#undef EMPTY
4995#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004996#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004997#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004998
4999/*
5000 * Open a file in noclobber mode.
5001 * The code was copied from bash.
5002 */
5003static int
5004noclobberopen(const char *fname)
5005{
5006 int r, fd;
5007 struct stat finfo, finfo2;
5008
5009 /*
5010 * If the file exists and is a regular file, return an error
5011 * immediately.
5012 */
5013 r = stat(fname, &finfo);
5014 if (r == 0 && S_ISREG(finfo.st_mode)) {
5015 errno = EEXIST;
5016 return -1;
5017 }
5018
5019 /*
5020 * If the file was not present (r != 0), make sure we open it
5021 * exclusively so that if it is created before we open it, our open
5022 * will fail. Make sure that we do not truncate an existing file.
5023 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5024 * file was not a regular file, we leave O_EXCL off.
5025 */
5026 if (r != 0)
5027 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5028 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5029
5030 /* If the open failed, return the file descriptor right away. */
5031 if (fd < 0)
5032 return fd;
5033
5034 /*
5035 * OK, the open succeeded, but the file may have been changed from a
5036 * non-regular file to a regular file between the stat and the open.
5037 * We are assuming that the O_EXCL open handles the case where FILENAME
5038 * did not exist and is symlinked to an existing file between the stat
5039 * and open.
5040 */
5041
5042 /*
5043 * If we can open it and fstat the file descriptor, and neither check
5044 * revealed that it was a regular file, and the file has not been
5045 * replaced, return the file descriptor.
5046 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005047 if (fstat(fd, &finfo2) == 0
5048 && !S_ISREG(finfo2.st_mode)
5049 && finfo.st_dev == finfo2.st_dev
5050 && finfo.st_ino == finfo2.st_ino
5051 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005052 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005053 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005054
5055 /* The file has been replaced. badness. */
5056 close(fd);
5057 errno = EEXIST;
5058 return -1;
5059}
5060
5061/*
5062 * Handle here documents. Normally we fork off a process to write the
5063 * data to a pipe. If the document is short, we can stuff the data in
5064 * the pipe without forking.
5065 */
5066/* openhere needs this forward reference */
5067static void expandhere(union node *arg, int fd);
5068static int
5069openhere(union node *redir)
5070{
5071 int pip[2];
5072 size_t len = 0;
5073
5074 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005075 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005076 if (redir->type == NHERE) {
5077 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005078 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005079 full_write(pip[1], redir->nhere.doc->narg.text, len);
5080 goto out;
5081 }
5082 }
5083 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005084 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005085 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005086 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5087 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5088 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5089 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005090 signal(SIGPIPE, SIG_DFL);
5091 if (redir->type == NHERE)
5092 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005093 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005094 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005095 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005096 }
5097 out:
5098 close(pip[1]);
5099 return pip[0];
5100}
5101
5102static int
5103openredirect(union node *redir)
5104{
5105 char *fname;
5106 int f;
5107
5108 switch (redir->nfile.type) {
5109 case NFROM:
5110 fname = redir->nfile.expfname;
5111 f = open(fname, O_RDONLY);
5112 if (f < 0)
5113 goto eopen;
5114 break;
5115 case NFROMTO:
5116 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005117 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005118 if (f < 0)
5119 goto ecreate;
5120 break;
5121 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005122#if ENABLE_ASH_BASH_COMPAT
5123 case NTO2:
5124#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005125 /* Take care of noclobber mode. */
5126 if (Cflag) {
5127 fname = redir->nfile.expfname;
5128 f = noclobberopen(fname);
5129 if (f < 0)
5130 goto ecreate;
5131 break;
5132 }
5133 /* FALLTHROUGH */
5134 case NCLOBBER:
5135 fname = redir->nfile.expfname;
5136 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5137 if (f < 0)
5138 goto ecreate;
5139 break;
5140 case NAPPEND:
5141 fname = redir->nfile.expfname;
5142 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5143 if (f < 0)
5144 goto ecreate;
5145 break;
5146 default:
5147#if DEBUG
5148 abort();
5149#endif
5150 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005151/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005152// case NTOFD:
5153// case NFROMFD:
5154// f = -1;
5155// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005156 case NHERE:
5157 case NXHERE:
5158 f = openhere(redir);
5159 break;
5160 }
5161
5162 return f;
5163 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005164 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005165 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005166 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005167}
5168
5169/*
5170 * Copy a file descriptor to be >= to. Returns -1
5171 * if the source file descriptor is closed, EMPTY if there are no unused
5172 * file descriptors left.
5173 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005174/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5175 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005176enum {
5177 COPYFD_EXACT = (int)~(INT_MAX),
5178 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5179};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005180static int
5181copyfd(int from, int to)
5182{
5183 int newfd;
5184
Denis Vlasenko5a867312008-07-24 19:46:38 +00005185 if (to & COPYFD_EXACT) {
5186 to &= ~COPYFD_EXACT;
5187 /*if (from != to)*/
5188 newfd = dup2(from, to);
5189 } else {
5190 newfd = fcntl(from, F_DUPFD, to);
5191 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005192 if (newfd < 0) {
5193 if (errno == EMFILE)
5194 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005195 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005196 ash_msg_and_raise_error("%d: %m", from);
5197 }
5198 return newfd;
5199}
5200
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005201/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005202struct two_fd_t {
5203 int orig, copy;
5204};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005205struct redirtab {
5206 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005207 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005208 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005209 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005210};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005211#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005212
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005213static int need_to_remember(struct redirtab *rp, int fd)
5214{
5215 int i;
5216
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005217 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005218 return 0;
5219
5220 for (i = 0; i < rp->pair_count; i++) {
5221 if (rp->two_fd[i].orig == fd) {
5222 /* already remembered */
5223 return 0;
5224 }
5225 }
5226 return 1;
5227}
5228
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005229/* "hidden" fd is a fd used to read scripts, or a copy of such */
5230static int is_hidden_fd(struct redirtab *rp, int fd)
5231{
5232 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005233 struct parsefile *pf;
5234
5235 if (fd == -1)
5236 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005237 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005238 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005239 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005240 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005241 * $ ash # running ash interactively
5242 * $ . ./script.sh
5243 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005244 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005245 * it's still ok to use it: "read" builtin uses it,
5246 * why should we cripple "exec" builtin?
5247 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005248 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005249 return 1;
5250 }
5251 pf = pf->prev;
5252 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005253
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005254 if (!rp)
5255 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005256 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005257 fd |= COPYFD_RESTORE;
5258 for (i = 0; i < rp->pair_count; i++) {
5259 if (rp->two_fd[i].copy == fd) {
5260 return 1;
5261 }
5262 }
5263 return 0;
5264}
5265
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005266/*
5267 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5268 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005269 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005270 */
5271/* flags passed to redirect */
5272#define REDIR_PUSH 01 /* save previous values of file descriptors */
5273#define REDIR_SAVEFD2 03 /* set preverrout */
5274static void
5275redirect(union node *redir, int flags)
5276{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005277 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005278 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005279 int i;
5280 int fd;
5281 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005282 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005283
Denis Vlasenko01631112007-12-16 17:20:38 +00005284 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005285 if (!redir) {
5286 return;
5287 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005288
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005289 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005290 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005291 INT_OFF;
5292 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005293 union node *tmp = redir;
5294 do {
5295 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005296#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005297 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005298 sv_pos++;
5299#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005300 tmp = tmp->nfile.next;
5301 } while (tmp);
5302 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005303 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005304 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005305 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005306 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005307 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005308 while (sv_pos > 0) {
5309 sv_pos--;
5310 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5311 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005312 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005313
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005314 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005315 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005316 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005317 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005318 right_fd = redir->ndup.dupfd;
5319 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005320 /* redirect from/to same file descriptor? */
5321 if (right_fd == fd)
5322 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005323 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005324 if (is_hidden_fd(sv, right_fd)) {
5325 errno = EBADF; /* as if it is closed */
5326 ash_msg_and_raise_error("%d: %m", right_fd);
5327 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005328 newfd = -1;
5329 } else {
5330 newfd = openredirect(redir); /* always >= 0 */
5331 if (fd == newfd) {
5332 /* Descriptor wasn't open before redirect.
5333 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005334 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005335 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005336 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005337 continue;
5338 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005339 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005340#if ENABLE_ASH_BASH_COMPAT
5341 redirect_more:
5342#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005343 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005344 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005345 /* Careful to not accidentally "save"
5346 * to the same fd as right side fd in N>&M */
5347 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5348 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005349/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5350 * are closed in popredir() in the child, preventing them from leaking
5351 * into child. (popredir() also cleans up the mess in case of failures)
5352 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005353 if (i == -1) {
5354 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005355 if (i != EBADF) {
5356 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005357 if (newfd >= 0)
5358 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005359 errno = i;
5360 ash_msg_and_raise_error("%d: %m", fd);
5361 /* NOTREACHED */
5362 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005363 /* EBADF: it is not open - good, remember to close it */
5364 remember_to_close:
5365 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005366 } else { /* fd is open, save its copy */
5367 /* "exec fd>&-" should not close fds
5368 * which point to script file(s).
5369 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005370 if (is_hidden_fd(sv, fd))
5371 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005372 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005373 if (fd == 2)
5374 copied_fd2 = i;
5375 sv->two_fd[sv_pos].orig = fd;
5376 sv->two_fd[sv_pos].copy = i;
5377 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005378 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005379 if (newfd < 0) {
5380 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005381 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005382 /* Don't want to trigger debugging */
5383 if (fd != -1)
5384 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005385 } else {
5386 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005387 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005388 } else if (fd != newfd) { /* move newfd to fd */
5389 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005390#if ENABLE_ASH_BASH_COMPAT
5391 if (!(redir->nfile.type == NTO2 && fd == 2))
5392#endif
5393 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005394 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005395#if ENABLE_ASH_BASH_COMPAT
5396 if (redir->nfile.type == NTO2 && fd == 1) {
5397 /* We already redirected it to fd 1, now copy it to 2 */
5398 newfd = 1;
5399 fd = 2;
5400 goto redirect_more;
5401 }
5402#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005403 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005404
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005405 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005406 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5407 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005408}
5409
5410/*
5411 * Undo the effects of the last redirection.
5412 */
5413static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005414popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005415{
5416 struct redirtab *rp;
5417 int i;
5418
Denis Vlasenko01631112007-12-16 17:20:38 +00005419 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005420 return;
5421 INT_OFF;
5422 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005423 for (i = 0; i < rp->pair_count; i++) {
5424 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005425 int copy = rp->two_fd[i].copy;
5426 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005427 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005428 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005429 continue;
5430 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005431 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005432 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005433 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005434 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005435 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005436 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005437 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005438 }
5439 }
5440 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005441 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005442 free(rp);
5443 INT_ON;
5444}
5445
5446/*
5447 * Undo all redirections. Called on error or interrupt.
5448 */
5449
5450/*
5451 * Discard all saved file descriptors.
5452 */
5453static void
5454clearredir(int drop)
5455{
5456 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005457 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005458 if (!redirlist)
5459 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005460 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005461 }
5462}
5463
5464static int
5465redirectsafe(union node *redir, int flags)
5466{
5467 int err;
5468 volatile int saveint;
5469 struct jmploc *volatile savehandler = exception_handler;
5470 struct jmploc jmploc;
5471
5472 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005473 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5474 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005475 if (!err) {
5476 exception_handler = &jmploc;
5477 redirect(redir, flags);
5478 }
5479 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005480 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005481 longjmp(exception_handler->loc, 1);
5482 RESTORE_INT(saveint);
5483 return err;
5484}
5485
5486
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005487/* ============ Routines to expand arguments to commands
5488 *
5489 * We have to deal with backquotes, shell variables, and file metacharacters.
5490 */
5491
Mike Frysinger98c52642009-04-02 10:02:37 +00005492#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005493static arith_t
5494ash_arith(const char *s)
5495{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005496 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005497 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005498
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005499 math_state.lookupvar = lookupvar;
5500 math_state.setvar = setvar2;
5501 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005502
5503 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005504 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005505 if (math_state.errmsg)
5506 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005507 INT_ON;
5508
5509 return result;
5510}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005511#endif
5512
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005513/*
5514 * expandarg flags
5515 */
5516#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5517#define EXP_TILDE 0x2 /* do normal tilde expansion */
5518#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5519#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5520#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5521#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5522#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5523#define EXP_WORD 0x80 /* expand word in parameter expansion */
5524#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5525/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005526 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005527 */
5528#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5529#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5530#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5531#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5532#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5533
5534/*
5535 * Structure specifying which parts of the string should be searched
5536 * for IFS characters.
5537 */
5538struct ifsregion {
5539 struct ifsregion *next; /* next region in list */
5540 int begoff; /* offset of start of region */
5541 int endoff; /* offset of end of region */
5542 int nulonly; /* search for nul bytes only */
5543};
5544
5545struct arglist {
5546 struct strlist *list;
5547 struct strlist **lastp;
5548};
5549
5550/* output of current string */
5551static char *expdest;
5552/* list of back quote expressions */
5553static struct nodelist *argbackq;
5554/* first struct in list of ifs regions */
5555static struct ifsregion ifsfirst;
5556/* last struct in list */
5557static struct ifsregion *ifslastp;
5558/* holds expanded arg list */
5559static struct arglist exparg;
5560
5561/*
5562 * Our own itoa().
5563 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005564#if !ENABLE_SH_MATH_SUPPORT
5565/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5566typedef long arith_t;
5567# define ARITH_FMT "%ld"
5568#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005569static int
5570cvtnum(arith_t num)
5571{
5572 int len;
5573
5574 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005575 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005576 STADJUST(len, expdest);
5577 return len;
5578}
5579
5580static size_t
5581esclen(const char *start, const char *p)
5582{
5583 size_t esc = 0;
5584
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005585 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005586 esc++;
5587 }
5588 return esc;
5589}
5590
5591/*
5592 * Remove any CTLESC characters from a string.
5593 */
5594static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005595rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005596{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005597 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005598
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005599 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005601 unsigned protect_against_glob;
5602 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005603
5604 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005605 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005607
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005608 q = p;
5609 r = str;
5610 if (flag & RMESCAPE_ALLOC) {
5611 size_t len = p - str;
5612 size_t fulllen = len + strlen(p) + 1;
5613
5614 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005615 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005616 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005617 /* p and str may be invalidated by makestrspace */
5618 str = (char *)stackblock() + strloc;
5619 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005620 } else if (flag & RMESCAPE_HEAP) {
5621 r = ckmalloc(fulllen);
5622 } else {
5623 r = stalloc(fulllen);
5624 }
5625 q = r;
5626 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005627 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005628 }
5629 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005630
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005631 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5632 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005633 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005634 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005635 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005636// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5637// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5638// Note: both inquotes and protect_against_glob only affect whether
5639// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005640 inquotes = ~inquotes;
5641 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005642 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005643 continue;
5644 }
5645 if (*p == '\\') {
5646 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005647 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648 goto copy;
5649 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005650 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005651 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005652 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005653 *q++ = '\\';
5654 }
5655 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005656 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005657 copy:
5658 *q++ = *p++;
5659 }
5660 *q = '\0';
5661 if (flag & RMESCAPE_GROW) {
5662 expdest = r;
5663 STADJUST(q - r + 1, expdest);
5664 }
5665 return r;
5666}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005667#define pmatch(a, b) !fnmatch((a), (b), 0)
5668
5669/*
5670 * Prepare a pattern for a expmeta (internal glob(3)) call.
5671 *
5672 * Returns an stalloced string.
5673 */
5674static char *
5675preglob(const char *pattern, int quoted, int flag)
5676{
5677 flag |= RMESCAPE_GLOB;
5678 if (quoted) {
5679 flag |= RMESCAPE_QUOTED;
5680 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005681 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005682}
5683
5684/*
5685 * Put a string on the stack.
5686 */
5687static void
5688memtodest(const char *p, size_t len, int syntax, int quotes)
5689{
5690 char *q = expdest;
5691
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005692 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005693
5694 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005695 unsigned char c = *p++;
5696 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005697 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005698 if (quotes) {
5699 int n = SIT(c, syntax);
5700 if (n == CCTL || n == CBACK)
5701 USTPUTC(CTLESC, q);
5702 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005703 USTPUTC(c, q);
5704 }
5705
5706 expdest = q;
5707}
5708
5709static void
5710strtodest(const char *p, int syntax, int quotes)
5711{
5712 memtodest(p, strlen(p), syntax, quotes);
5713}
5714
5715/*
5716 * Record the fact that we have to scan this region of the
5717 * string for IFS characters.
5718 */
5719static void
5720recordregion(int start, int end, int nulonly)
5721{
5722 struct ifsregion *ifsp;
5723
5724 if (ifslastp == NULL) {
5725 ifsp = &ifsfirst;
5726 } else {
5727 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005728 ifsp = ckzalloc(sizeof(*ifsp));
5729 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005730 ifslastp->next = ifsp;
5731 INT_ON;
5732 }
5733 ifslastp = ifsp;
5734 ifslastp->begoff = start;
5735 ifslastp->endoff = end;
5736 ifslastp->nulonly = nulonly;
5737}
5738
5739static void
5740removerecordregions(int endoff)
5741{
5742 if (ifslastp == NULL)
5743 return;
5744
5745 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005746 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005747 struct ifsregion *ifsp;
5748 INT_OFF;
5749 ifsp = ifsfirst.next->next;
5750 free(ifsfirst.next);
5751 ifsfirst.next = ifsp;
5752 INT_ON;
5753 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005754 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005755 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005756 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005757 ifslastp = &ifsfirst;
5758 ifsfirst.endoff = endoff;
5759 }
5760 return;
5761 }
5762
5763 ifslastp = &ifsfirst;
5764 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005765 ifslastp = ifslastp->next;
5766 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005767 struct ifsregion *ifsp;
5768 INT_OFF;
5769 ifsp = ifslastp->next->next;
5770 free(ifslastp->next);
5771 ifslastp->next = ifsp;
5772 INT_ON;
5773 }
5774 if (ifslastp->endoff > endoff)
5775 ifslastp->endoff = endoff;
5776}
5777
5778static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005779exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005780{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005781 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005782 char *name;
5783 struct passwd *pw;
5784 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005785 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005786 int startloc;
5787
5788 name = p + 1;
5789
5790 while ((c = *++p) != '\0') {
5791 switch (c) {
5792 case CTLESC:
5793 return startp;
5794 case CTLQUOTEMARK:
5795 return startp;
5796 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005797 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005798 goto done;
5799 break;
5800 case '/':
5801 case CTLENDVAR:
5802 goto done;
5803 }
5804 }
5805 done:
5806 *p = '\0';
5807 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005808 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005809 } else {
5810 pw = getpwnam(name);
5811 if (pw == NULL)
5812 goto lose;
5813 home = pw->pw_dir;
5814 }
5815 if (!home || !*home)
5816 goto lose;
5817 *p = c;
5818 startloc = expdest - (char *)stackblock();
5819 strtodest(home, SQSYNTAX, quotes);
5820 recordregion(startloc, expdest - (char *)stackblock(), 0);
5821 return p;
5822 lose:
5823 *p = c;
5824 return startp;
5825}
5826
5827/*
5828 * Execute a command inside back quotes. If it's a builtin command, we
5829 * want to save its output in a block obtained from malloc. Otherwise
5830 * we fork off a subprocess and get the output of the command via a pipe.
5831 * Should be called with interrupts off.
5832 */
5833struct backcmd { /* result of evalbackcmd */
5834 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005835 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005836 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005837 struct job *jp; /* job structure for command */
5838};
5839
5840/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005841static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005842#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005843static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005844
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005845static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005846evalbackcmd(union node *n, struct backcmd *result)
5847{
5848 int saveherefd;
5849
5850 result->fd = -1;
5851 result->buf = NULL;
5852 result->nleft = 0;
5853 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005854 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005855 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856
5857 saveherefd = herefd;
5858 herefd = -1;
5859
5860 {
5861 int pip[2];
5862 struct job *jp;
5863
5864 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005865 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005866 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005867 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5868 FORCE_INT_ON;
5869 close(pip[0]);
5870 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005871 /*close(1);*/
5872 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005873 close(pip[1]);
5874 }
5875 eflag = 0;
5876 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5877 /* NOTREACHED */
5878 }
5879 close(pip[1]);
5880 result->fd = pip[0];
5881 result->jp = jp;
5882 }
5883 herefd = saveherefd;
5884 out:
5885 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5886 result->fd, result->buf, result->nleft, result->jp));
5887}
5888
5889/*
5890 * Expand stuff in backwards quotes.
5891 */
5892static void
5893expbackq(union node *cmd, int quoted, int quotes)
5894{
5895 struct backcmd in;
5896 int i;
5897 char buf[128];
5898 char *p;
5899 char *dest;
5900 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005901 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005902 struct stackmark smark;
5903
5904 INT_OFF;
5905 setstackmark(&smark);
5906 dest = expdest;
5907 startloc = dest - (char *)stackblock();
5908 grabstackstr(dest);
5909 evalbackcmd(cmd, &in);
5910 popstackmark(&smark);
5911
5912 p = in.buf;
5913 i = in.nleft;
5914 if (i == 0)
5915 goto read;
5916 for (;;) {
5917 memtodest(p, i, syntax, quotes);
5918 read:
5919 if (in.fd < 0)
5920 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005921 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005922 TRACE(("expbackq: read returns %d\n", i));
5923 if (i <= 0)
5924 break;
5925 p = buf;
5926 }
5927
Denis Vlasenko60818682007-09-28 22:07:23 +00005928 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005929 if (in.fd >= 0) {
5930 close(in.fd);
5931 back_exitstatus = waitforjob(in.jp);
5932 }
5933 INT_ON;
5934
5935 /* Eat all trailing newlines */
5936 dest = expdest;
5937 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5938 STUNPUTC(dest);
5939 expdest = dest;
5940
5941 if (quoted == 0)
5942 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005943 TRACE(("evalbackq: size:%d:'%.*s'\n",
5944 (int)((dest - (char *)stackblock()) - startloc),
5945 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005946 stackblock() + startloc));
5947}
5948
Mike Frysinger98c52642009-04-02 10:02:37 +00005949#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950/*
5951 * Expand arithmetic expression. Backup to start of expression,
5952 * evaluate, place result in (backed up) result, adjust string position.
5953 */
5954static void
5955expari(int quotes)
5956{
5957 char *p, *start;
5958 int begoff;
5959 int flag;
5960 int len;
5961
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005962 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005963
5964 /*
5965 * This routine is slightly over-complicated for
5966 * efficiency. Next we scan backwards looking for the
5967 * start of arithmetic.
5968 */
5969 start = stackblock();
5970 p = expdest - 1;
5971 *p = '\0';
5972 p--;
5973 do {
5974 int esc;
5975
Denys Vlasenkocd716832009-11-28 22:14:02 +01005976 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977 p--;
5978#if DEBUG
5979 if (p < start) {
5980 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5981 }
5982#endif
5983 }
5984
5985 esc = esclen(start, p);
5986 if (!(esc % 2)) {
5987 break;
5988 }
5989
5990 p -= esc + 1;
5991 } while (1);
5992
5993 begoff = p - start;
5994
5995 removerecordregions(begoff);
5996
5997 flag = p[1];
5998
5999 expdest = p;
6000
6001 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006002 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006004 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005
6006 if (flag != '"')
6007 recordregion(begoff, begoff + len, 0);
6008}
6009#endif
6010
6011/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006012static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006013
6014/*
6015 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6016 * characters to allow for further processing. Otherwise treat
6017 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006018 *
6019 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6020 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6021 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006022 */
6023static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006024argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006026 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 '=',
6028 ':',
6029 CTLQUOTEMARK,
6030 CTLENDVAR,
6031 CTLESC,
6032 CTLVAR,
6033 CTLBACKQ,
6034 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006035#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036 CTLENDARI,
6037#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006038 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 };
6040 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006041 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6042 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 int inquotes;
6044 size_t length;
6045 int startloc;
6046
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006047 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006049 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006050 reject++;
6051 }
6052 inquotes = 0;
6053 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006054 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006055 char *q;
6056
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006057 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006058 tilde:
6059 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006060 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006061 q++;
6062 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006063 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006064 }
6065 start:
6066 startloc = expdest - (char *)stackblock();
6067 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006068 unsigned char c;
6069
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006070 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006071 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006072 if (c) {
6073 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006074 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006075 ) {
6076 /* c == '=' || c == ':' || c == CTLENDARI */
6077 length++;
6078 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006079 }
6080 if (length > 0) {
6081 int newloc;
6082 expdest = stack_nputstr(p, length, expdest);
6083 newloc = expdest - (char *)stackblock();
6084 if (breakall && !inquotes && newloc > startloc) {
6085 recordregion(startloc, newloc, 0);
6086 }
6087 startloc = newloc;
6088 }
6089 p += length + 1;
6090 length = 0;
6091
6092 switch (c) {
6093 case '\0':
6094 goto breakloop;
6095 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006096 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006097 p--;
6098 continue;
6099 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006100 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006101 reject++;
6102 /* fall through */
6103 case ':':
6104 /*
6105 * sort of a hack - expand tildes in variable
6106 * assignments (after the first '=' and after ':'s).
6107 */
6108 if (*--p == '~') {
6109 goto tilde;
6110 }
6111 continue;
6112 }
6113
6114 switch (c) {
6115 case CTLENDVAR: /* ??? */
6116 goto breakloop;
6117 case CTLQUOTEMARK:
6118 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006119 if (!inquotes
6120 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006121 && ( p[4] == (char)CTLQUOTEMARK
6122 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006123 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006124 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006125 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 goto start;
6127 }
6128 inquotes = !inquotes;
6129 addquote:
6130 if (quotes) {
6131 p--;
6132 length++;
6133 startloc++;
6134 }
6135 break;
6136 case CTLESC:
6137 startloc++;
6138 length++;
6139 goto addquote;
6140 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006141 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006142 goto start;
6143 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006144 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 case CTLBACKQ|CTLQUOTE:
6146 expbackq(argbackq->n, c, quotes);
6147 argbackq = argbackq->next;
6148 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006149#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006150 case CTLENDARI:
6151 p--;
6152 expari(quotes);
6153 goto start;
6154#endif
6155 }
6156 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006157 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006158}
6159
6160static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006161scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6162 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006163{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006164 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006165 char c;
6166
6167 loc = startp;
6168 loc2 = rmesc;
6169 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006170 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006171 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006172
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006173 c = *loc2;
6174 if (zero) {
6175 *loc2 = '\0';
6176 s = rmesc;
6177 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006178 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006179
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006180 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006181 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006182 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006183 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 loc++;
6185 loc++;
6186 loc2++;
6187 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006188 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006189}
6190
6191static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006192scanright(char *startp, char *rmesc, char *rmescend,
6193 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006194{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006195#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6196 int try2optimize = match_at_start;
6197#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006198 int esc = 0;
6199 char *loc;
6200 char *loc2;
6201
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006202 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6203 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6204 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6205 * Logic:
6206 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6207 * and on each iteration they go back two/one char until they reach the beginning.
6208 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6209 */
6210 /* TODO: document in what other circumstances we are called. */
6211
6212 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213 int match;
6214 char c = *loc2;
6215 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006216 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 *loc2 = '\0';
6218 s = rmesc;
6219 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006220 match = pmatch(pattern, s);
6221 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006222 *loc2 = c;
6223 if (match)
6224 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006225#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6226 if (try2optimize) {
6227 /* Maybe we can optimize this:
6228 * if pattern ends with unescaped *, we can avoid checking
6229 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6230 * it wont match truncated "raw_value_of_" strings too.
6231 */
6232 unsigned plen = strlen(pattern);
6233 /* Does it end with "*"? */
6234 if (plen != 0 && pattern[--plen] == '*') {
6235 /* "xxxx*" is not escaped */
6236 /* "xxx\*" is escaped */
6237 /* "xx\\*" is not escaped */
6238 /* "x\\\*" is escaped */
6239 int slashes = 0;
6240 while (plen != 0 && pattern[--plen] == '\\')
6241 slashes++;
6242 if (!(slashes & 1))
6243 break; /* ends with unescaped "*" */
6244 }
6245 try2optimize = 0;
6246 }
6247#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006248 loc--;
6249 if (quotes) {
6250 if (--esc < 0) {
6251 esc = esclen(startp, loc);
6252 }
6253 if (esc % 2) {
6254 esc--;
6255 loc--;
6256 }
6257 }
6258 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006259 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006260}
6261
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006262static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263static void
6264varunset(const char *end, const char *var, const char *umsg, int varflags)
6265{
6266 const char *msg;
6267 const char *tail;
6268
6269 tail = nullstr;
6270 msg = "parameter not set";
6271 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006272 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006273 if (varflags & VSNUL)
6274 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006275 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006276 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006277 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006278 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006279 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280}
6281
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006282#if ENABLE_ASH_BASH_COMPAT
6283static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006284parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006285{
6286 char *idx, *repl = NULL;
6287 unsigned char c;
6288
Denys Vlasenko16149002010-08-06 22:06:21 +02006289 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006290 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006291 idx = arg;
6292 while (1) {
6293 c = *arg;
6294 if (!c)
6295 break;
6296 if (c == '/') {
6297 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006298 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006299 repl = idx + 1;
6300 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006301 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006302 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006303 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006304 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006305 /*
6306 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6307 * The result is a_\_z_c (not a\_\_z_c)!
6308 *
6309 * Enable debug prints in this function and you'll see:
6310 * ash: arg:'\\b/_\\_z_' varflags:d
6311 * ash: pattern:'\\b' repl:'_\_z_'
6312 * That is, \\b is interpreted as \\b, but \\_ as \_!
6313 * IOW: search pattern and replace string treat backslashes
6314 * differently! That is the reason why we check repl below:
6315 */
6316 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6317 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006318 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006319 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006320 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006321
6322 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323}
6324#endif /* ENABLE_ASH_BASH_COMPAT */
6325
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006327subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006328 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006330 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006331 char *startp;
6332 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006333 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006334 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006335 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006336 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006337 int saveherefd = herefd;
6338 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006339 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006340 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006341
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006342 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6343 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006344
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006345 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006346 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6347 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006348 STPUTC('\0', expdest);
6349 herefd = saveherefd;
6350 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006351 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006352
6353 switch (subtype) {
6354 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006355 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 amount = startp - expdest;
6357 STADJUST(amount, expdest);
6358 return startp;
6359
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006360 case VSQUESTION:
6361 varunset(p, varname, startp, varflags);
6362 /* NOTREACHED */
6363
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006364#if ENABLE_ASH_BASH_COMPAT
6365 case VSSUBSTR:
6366 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006367 /* Read POS in ${var:POS:LEN} */
6368 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006369 len = str - startp - 1;
6370
6371 /* *loc != '\0', guaranteed by parser */
6372 if (quotes) {
6373 char *ptr;
6374
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006375 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006376 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006377 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006378 len--;
6379 ptr++;
6380 }
6381 }
6382 }
6383 orig_len = len;
6384
6385 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006386 /* ${var::LEN} */
6387 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006388 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006389 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006390 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006391 while (*loc && *loc != ':') {
6392 /* TODO?
6393 * bash complains on: var=qwe; echo ${var:1a:123}
6394 if (!isdigit(*loc))
6395 ash_msg_and_raise_error(msg_illnum, str);
6396 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006397 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006398 }
6399 if (*loc++ == ':') {
6400 len = number(loc);
6401 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006402 }
6403 if (pos >= orig_len) {
6404 pos = 0;
6405 len = 0;
6406 }
6407 if (len > (orig_len - pos))
6408 len = orig_len - pos;
6409
6410 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006411 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006412 str++;
6413 }
6414 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006415 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006416 *loc++ = *str++;
6417 *loc++ = *str++;
6418 }
6419 *loc = '\0';
6420 amount = loc - expdest;
6421 STADJUST(amount, expdest);
6422 return loc;
6423#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006424 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006425
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006426 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 /* We'll comeback here if we grow the stack while handling
6429 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6430 * stack will need rebasing, and we'll need to remove our work
6431 * areas each time
6432 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006433 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006434
6435 amount = expdest - ((char *)stackblock() + resetloc);
6436 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006437 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006438
6439 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006440 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006442 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006443 if (rmesc != startp) {
6444 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006445 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006446 }
6447 }
6448 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006449 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006450 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006451 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006452
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006453#if ENABLE_ASH_BASH_COMPAT
6454 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006455 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006456
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006457 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006458 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006459 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006461 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006462 }
6463
6464 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006465 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006466 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006467
6468 len = 0;
6469 idx = startp;
6470 end = str - 1;
6471 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006472 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006473 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006474 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475 if (!loc) {
6476 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006477 char *restart_detect = stackblock();
6478 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006479 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006480 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006481 idx++;
6482 len++;
6483 STPUTC(*idx, expdest);
6484 }
6485 if (stackblock() != restart_detect)
6486 goto restart;
6487 idx++;
6488 len++;
6489 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006490 /* continue; - prone to quadratic behavior, smarter code: */
6491 if (idx >= end)
6492 break;
6493 if (str[0] == '*') {
6494 /* Pattern is "*foo". If "*foo" does not match "long_string",
6495 * it would never match "ong_string" etc, no point in trying.
6496 */
6497 goto skip_matching;
6498 }
6499 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006500 }
6501
6502 if (subtype == VSREPLACEALL) {
6503 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006504 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006505 idx++;
6506 idx++;
6507 rmesc++;
6508 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006509 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006510 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006511 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006512
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006513 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006514 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006515 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006516 if (quotes && *loc == '\\') {
6517 STPUTC(CTLESC, expdest);
6518 len++;
6519 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006520 STPUTC(*loc, expdest);
6521 if (stackblock() != restart_detect)
6522 goto restart;
6523 len++;
6524 }
6525
6526 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006527 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006528 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006529 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006530 STPUTC(*idx, expdest);
6531 if (stackblock() != restart_detect)
6532 goto restart;
6533 len++;
6534 idx++;
6535 }
6536 break;
6537 }
6538 }
6539
6540 /* We've put the replaced text into a buffer at workloc, now
6541 * move it to the right place and adjust the stack.
6542 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006543 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006544 startp = (char *)stackblock() + startloc;
6545 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006546 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006547 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006548 STADJUST(-amount, expdest);
6549 return startp;
6550 }
6551#endif /* ENABLE_ASH_BASH_COMPAT */
6552
6553 subtype -= VSTRIMRIGHT;
6554#if DEBUG
6555 if (subtype < 0 || subtype > 7)
6556 abort();
6557#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006558 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006559 zero = subtype >> 1;
6560 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6561 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6562
6563 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6564 if (loc) {
6565 if (zero) {
6566 memmove(startp, loc, str - loc);
6567 loc = startp + (str - loc) - 1;
6568 }
6569 *loc = '\0';
6570 amount = loc - expdest;
6571 STADJUST(amount, expdest);
6572 }
6573 return loc;
6574}
6575
6576/*
6577 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006578 * name parameter (examples):
6579 * ash -c 'echo $1' name:'1='
6580 * ash -c 'echo $qwe' name:'qwe='
6581 * ash -c 'echo $$' name:'$='
6582 * ash -c 'echo ${$}' name:'$='
6583 * ash -c 'echo ${$##q}' name:'$=q'
6584 * ash -c 'echo ${#$}' name:'$='
6585 * note: examples with bad shell syntax:
6586 * ash -c 'echo ${#$1}' name:'$=1'
6587 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006589static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006590varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006591{
Mike Frysinger98c52642009-04-02 10:02:37 +00006592 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006593 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595 int sepq = 0;
6596 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006598 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006599 int quoted = varflags & VSQUOTE;
6600 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006601
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006602 switch (*name) {
6603 case '$':
6604 num = rootpid;
6605 goto numvar;
6606 case '?':
6607 num = exitstatus;
6608 goto numvar;
6609 case '#':
6610 num = shellparam.nparam;
6611 goto numvar;
6612 case '!':
6613 num = backgndpid;
6614 if (num == 0)
6615 return -1;
6616 numvar:
6617 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006618 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006619 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006620 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006621 for (i = NOPTS - 1; i >= 0; i--) {
6622 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006623 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006624 len++;
6625 }
6626 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006627 check_1char_name:
6628#if 0
6629 /* handles cases similar to ${#$1} */
6630 if (name[2] != '\0')
6631 raise_error_syntax("bad substitution");
6632#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006633 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006634 case '@': {
6635 char **ap;
6636 int sep;
6637
6638 if (quoted && (flags & EXP_FULL)) {
6639 /* note: this is not meant as PEOF value */
6640 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006642 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006643 /* fall through */
6644 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006645 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006646 i = SIT(sep, syntax);
6647 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006648 sepq = 1;
6649 param:
6650 ap = shellparam.p;
6651 if (!ap)
6652 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006653 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006654 size_t partlen;
6655
6656 partlen = strlen(p);
6657 len += partlen;
6658
6659 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6660 memtodest(p, partlen, syntax, quotes);
6661
6662 if (*ap && sep) {
6663 char *q;
6664
6665 len++;
6666 if (subtype == VSPLUS || subtype == VSLENGTH) {
6667 continue;
6668 }
6669 q = expdest;
6670 if (sepq)
6671 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006672 /* note: may put NUL despite sep != 0
6673 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006674 STPUTC(sep, q);
6675 expdest = q;
6676 }
6677 }
6678 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006679 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006680 case '0':
6681 case '1':
6682 case '2':
6683 case '3':
6684 case '4':
6685 case '5':
6686 case '6':
6687 case '7':
6688 case '8':
6689 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006690 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006691 if (num < 0 || num > shellparam.nparam)
6692 return -1;
6693 p = num ? shellparam.p[num - 1] : arg0;
6694 goto value;
6695 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006696 /* NB: name has form "VAR=..." */
6697
6698 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6699 * which should be considered before we check variables. */
6700 if (var_str_list) {
6701 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6702 p = NULL;
6703 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006704 char *str, *eq;
6705 str = var_str_list->text;
6706 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006707 if (!eq) /* stop at first non-assignment */
6708 break;
6709 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006710 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006711 && strncmp(str, name, name_len) == 0
6712 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006713 p = eq;
6714 /* goto value; - WRONG! */
6715 /* think "A=1 A=2 B=$A" */
6716 }
6717 var_str_list = var_str_list->next;
6718 } while (var_str_list);
6719 if (p)
6720 goto value;
6721 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006722 p = lookupvar(name);
6723 value:
6724 if (!p)
6725 return -1;
6726
6727 len = strlen(p);
6728 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6729 memtodest(p, len, syntax, quotes);
6730 return len;
6731 }
6732
6733 if (subtype == VSPLUS || subtype == VSLENGTH)
6734 STADJUST(-len, expdest);
6735 return len;
6736}
6737
6738/*
6739 * Expand a variable, and return a pointer to the next character in the
6740 * input string.
6741 */
6742static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006743evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006745 char varflags;
6746 char subtype;
6747 char quoted;
6748 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006749 char *var;
6750 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006751 int startloc;
6752 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006753
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006754 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006755 subtype = varflags & VSTYPE;
6756 quoted = varflags & VSQUOTE;
6757 var = p;
6758 easy = (!quoted || (*var == '@' && shellparam.nparam));
6759 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006760 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006761
6762 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006763 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006764 if (varflags & VSNUL)
6765 varlen--;
6766
6767 if (subtype == VSPLUS) {
6768 varlen = -1 - varlen;
6769 goto vsplus;
6770 }
6771
6772 if (subtype == VSMINUS) {
6773 vsplus:
6774 if (varlen < 0) {
6775 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006776 p,
6777 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006778 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006779 );
6780 goto end;
6781 }
6782 if (easy)
6783 goto record;
6784 goto end;
6785 }
6786
6787 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6788 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006789 if (subevalvar(p, var, /* strloc: */ 0,
6790 subtype, startloc, varflags,
6791 /* quotes: */ 0,
6792 var_str_list)
6793 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006794 varflags &= ~VSNUL;
6795 /*
6796 * Remove any recorded regions beyond
6797 * start of variable
6798 */
6799 removerecordregions(startloc);
6800 goto again;
6801 }
6802 goto end;
6803 }
6804 if (easy)
6805 goto record;
6806 goto end;
6807 }
6808
6809 if (varlen < 0 && uflag)
6810 varunset(p, var, 0, 0);
6811
6812 if (subtype == VSLENGTH) {
6813 cvtnum(varlen > 0 ? varlen : 0);
6814 goto record;
6815 }
6816
6817 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006818 if (easy)
6819 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006820 goto end;
6821 }
6822
6823#if DEBUG
6824 switch (subtype) {
6825 case VSTRIMLEFT:
6826 case VSTRIMLEFTMAX:
6827 case VSTRIMRIGHT:
6828 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006829#if ENABLE_ASH_BASH_COMPAT
6830 case VSSUBSTR:
6831 case VSREPLACE:
6832 case VSREPLACEALL:
6833#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006834 break;
6835 default:
6836 abort();
6837 }
6838#endif
6839
6840 if (varlen >= 0) {
6841 /*
6842 * Terminate the string and start recording the pattern
6843 * right after it
6844 */
6845 STPUTC('\0', expdest);
6846 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006847 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006848 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006849//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006850 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006851 var_str_list)
6852 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006853 int amount = expdest - (
6854 (char *)stackblock() + patloc - 1
6855 );
6856 STADJUST(-amount, expdest);
6857 }
6858 /* Remove any recorded regions beyond start of variable */
6859 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006860 record:
6861 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006862 }
6863
6864 end:
6865 if (subtype != VSNORMAL) { /* skip to end of alternative */
6866 int nesting = 1;
6867 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006868 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006869 if (c == CTLESC)
6870 p++;
6871 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6872 if (varlen >= 0)
6873 argbackq = argbackq->next;
6874 } else if (c == CTLVAR) {
6875 if ((*p++ & VSTYPE) != VSNORMAL)
6876 nesting++;
6877 } else if (c == CTLENDVAR) {
6878 if (--nesting == 0)
6879 break;
6880 }
6881 }
6882 }
6883 return p;
6884}
6885
6886/*
6887 * Break the argument string into pieces based upon IFS and add the
6888 * strings to the argument list. The regions of the string to be
6889 * searched for IFS characters have been stored by recordregion.
6890 */
6891static void
6892ifsbreakup(char *string, struct arglist *arglist)
6893{
6894 struct ifsregion *ifsp;
6895 struct strlist *sp;
6896 char *start;
6897 char *p;
6898 char *q;
6899 const char *ifs, *realifs;
6900 int ifsspc;
6901 int nulonly;
6902
6903 start = string;
6904 if (ifslastp != NULL) {
6905 ifsspc = 0;
6906 nulonly = 0;
6907 realifs = ifsset() ? ifsval() : defifs;
6908 ifsp = &ifsfirst;
6909 do {
6910 p = string + ifsp->begoff;
6911 nulonly = ifsp->nulonly;
6912 ifs = nulonly ? nullstr : realifs;
6913 ifsspc = 0;
6914 while (p < string + ifsp->endoff) {
6915 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006916 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006917 p++;
6918 if (!strchr(ifs, *p)) {
6919 p++;
6920 continue;
6921 }
6922 if (!nulonly)
6923 ifsspc = (strchr(defifs, *p) != NULL);
6924 /* Ignore IFS whitespace at start */
6925 if (q == start && ifsspc) {
6926 p++;
6927 start = p;
6928 continue;
6929 }
6930 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006931 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006932 sp->text = start;
6933 *arglist->lastp = sp;
6934 arglist->lastp = &sp->next;
6935 p++;
6936 if (!nulonly) {
6937 for (;;) {
6938 if (p >= string + ifsp->endoff) {
6939 break;
6940 }
6941 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006942 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006943 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006944 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006945 p = q;
6946 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006947 }
6948 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006949 if (ifsspc) {
6950 p++;
6951 ifsspc = 0;
6952 } else {
6953 p = q;
6954 break;
6955 }
6956 } else
6957 p++;
6958 }
6959 }
6960 start = p;
6961 } /* while */
6962 ifsp = ifsp->next;
6963 } while (ifsp != NULL);
6964 if (nulonly)
6965 goto add;
6966 }
6967
6968 if (!*start)
6969 return;
6970
6971 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006972 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006973 sp->text = start;
6974 *arglist->lastp = sp;
6975 arglist->lastp = &sp->next;
6976}
6977
6978static void
6979ifsfree(void)
6980{
6981 struct ifsregion *p;
6982
6983 INT_OFF;
6984 p = ifsfirst.next;
6985 do {
6986 struct ifsregion *ifsp;
6987 ifsp = p->next;
6988 free(p);
6989 p = ifsp;
6990 } while (p);
6991 ifslastp = NULL;
6992 ifsfirst.next = NULL;
6993 INT_ON;
6994}
6995
6996/*
6997 * Add a file name to the list.
6998 */
6999static void
7000addfname(const char *name)
7001{
7002 struct strlist *sp;
7003
Denis Vlasenko597906c2008-02-20 16:38:54 +00007004 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007005 sp->text = ststrdup(name);
7006 *exparg.lastp = sp;
7007 exparg.lastp = &sp->next;
7008}
7009
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007010/*
7011 * Do metacharacter (i.e. *, ?, [...]) expansion.
7012 */
7013static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007014expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007015{
7016 char *p;
7017 const char *cp;
7018 char *start;
7019 char *endname;
7020 int metaflag;
7021 struct stat statb;
7022 DIR *dirp;
7023 struct dirent *dp;
7024 int atend;
7025 int matchdot;
7026
7027 metaflag = 0;
7028 start = name;
7029 for (p = name; *p; p++) {
7030 if (*p == '*' || *p == '?')
7031 metaflag = 1;
7032 else if (*p == '[') {
7033 char *q = p + 1;
7034 if (*q == '!')
7035 q++;
7036 for (;;) {
7037 if (*q == '\\')
7038 q++;
7039 if (*q == '/' || *q == '\0')
7040 break;
7041 if (*++q == ']') {
7042 metaflag = 1;
7043 break;
7044 }
7045 }
7046 } else if (*p == '\\')
7047 p++;
7048 else if (*p == '/') {
7049 if (metaflag)
7050 goto out;
7051 start = p + 1;
7052 }
7053 }
7054 out:
7055 if (metaflag == 0) { /* we've reached the end of the file name */
7056 if (enddir != expdir)
7057 metaflag++;
7058 p = name;
7059 do {
7060 if (*p == '\\')
7061 p++;
7062 *enddir++ = *p;
7063 } while (*p++);
7064 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7065 addfname(expdir);
7066 return;
7067 }
7068 endname = p;
7069 if (name < start) {
7070 p = name;
7071 do {
7072 if (*p == '\\')
7073 p++;
7074 *enddir++ = *p++;
7075 } while (p < start);
7076 }
7077 if (enddir == expdir) {
7078 cp = ".";
7079 } else if (enddir == expdir + 1 && *expdir == '/') {
7080 cp = "/";
7081 } else {
7082 cp = expdir;
7083 enddir[-1] = '\0';
7084 }
7085 dirp = opendir(cp);
7086 if (dirp == NULL)
7087 return;
7088 if (enddir != expdir)
7089 enddir[-1] = '/';
7090 if (*endname == 0) {
7091 atend = 1;
7092 } else {
7093 atend = 0;
7094 *endname++ = '\0';
7095 }
7096 matchdot = 0;
7097 p = start;
7098 if (*p == '\\')
7099 p++;
7100 if (*p == '.')
7101 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007102 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007103 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007104 continue;
7105 if (pmatch(start, dp->d_name)) {
7106 if (atend) {
7107 strcpy(enddir, dp->d_name);
7108 addfname(expdir);
7109 } else {
7110 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7111 continue;
7112 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007113 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007114 }
7115 }
7116 }
7117 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007118 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007119 endname[-1] = '/';
7120}
7121
7122static struct strlist *
7123msort(struct strlist *list, int len)
7124{
7125 struct strlist *p, *q = NULL;
7126 struct strlist **lpp;
7127 int half;
7128 int n;
7129
7130 if (len <= 1)
7131 return list;
7132 half = len >> 1;
7133 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007134 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007135 q = p;
7136 p = p->next;
7137 }
7138 q->next = NULL; /* terminate first half of list */
7139 q = msort(list, half); /* sort first half of list */
7140 p = msort(p, len - half); /* sort second half */
7141 lpp = &list;
7142 for (;;) {
7143#if ENABLE_LOCALE_SUPPORT
7144 if (strcoll(p->text, q->text) < 0)
7145#else
7146 if (strcmp(p->text, q->text) < 0)
7147#endif
7148 {
7149 *lpp = p;
7150 lpp = &p->next;
7151 p = *lpp;
7152 if (p == NULL) {
7153 *lpp = q;
7154 break;
7155 }
7156 } else {
7157 *lpp = q;
7158 lpp = &q->next;
7159 q = *lpp;
7160 if (q == NULL) {
7161 *lpp = p;
7162 break;
7163 }
7164 }
7165 }
7166 return list;
7167}
7168
7169/*
7170 * Sort the results of file name expansion. It calculates the number of
7171 * strings to sort and then calls msort (short for merge sort) to do the
7172 * work.
7173 */
7174static struct strlist *
7175expsort(struct strlist *str)
7176{
7177 int len;
7178 struct strlist *sp;
7179
7180 len = 0;
7181 for (sp = str; sp; sp = sp->next)
7182 len++;
7183 return msort(str, len);
7184}
7185
7186static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007187expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007188{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007189 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007190 '*', '?', '[', 0
7191 };
7192 /* TODO - EXP_REDIR */
7193
7194 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007195 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007196 struct strlist **savelastp;
7197 struct strlist *sp;
7198 char *p;
7199
7200 if (fflag)
7201 goto nometa;
7202 if (!strpbrk(str->text, metachars))
7203 goto nometa;
7204 savelastp = exparg.lastp;
7205
7206 INT_OFF;
7207 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7208 {
7209 int i = strlen(str->text);
7210 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7211 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007212 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007213 free(expdir);
7214 if (p != str->text)
7215 free(p);
7216 INT_ON;
7217 if (exparg.lastp == savelastp) {
7218 /*
7219 * no matches
7220 */
7221 nometa:
7222 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007223 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007224 exparg.lastp = &str->next;
7225 } else {
7226 *exparg.lastp = NULL;
7227 *savelastp = sp = expsort(*savelastp);
7228 while (sp->next != NULL)
7229 sp = sp->next;
7230 exparg.lastp = &sp->next;
7231 }
7232 str = str->next;
7233 }
7234}
7235
7236/*
7237 * Perform variable substitution and command substitution on an argument,
7238 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7239 * perform splitting and file name expansion. When arglist is NULL, perform
7240 * here document expansion.
7241 */
7242static void
7243expandarg(union node *arg, struct arglist *arglist, int flag)
7244{
7245 struct strlist *sp;
7246 char *p;
7247
7248 argbackq = arg->narg.backquote;
7249 STARTSTACKSTR(expdest);
7250 ifsfirst.next = NULL;
7251 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007252 argstr(arg->narg.text, flag,
7253 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007254 p = _STPUTC('\0', expdest);
7255 expdest = p - 1;
7256 if (arglist == NULL) {
7257 return; /* here document expanded */
7258 }
7259 p = grabstackstr(p);
7260 exparg.lastp = &exparg.list;
7261 /*
7262 * TODO - EXP_REDIR
7263 */
7264 if (flag & EXP_FULL) {
7265 ifsbreakup(p, &exparg);
7266 *exparg.lastp = NULL;
7267 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007268 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007269 } else {
7270 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007271 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007272 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007273 sp->text = p;
7274 *exparg.lastp = sp;
7275 exparg.lastp = &sp->next;
7276 }
7277 if (ifsfirst.next)
7278 ifsfree();
7279 *exparg.lastp = NULL;
7280 if (exparg.list) {
7281 *arglist->lastp = exparg.list;
7282 arglist->lastp = exparg.lastp;
7283 }
7284}
7285
7286/*
7287 * Expand shell variables and backquotes inside a here document.
7288 */
7289static void
7290expandhere(union node *arg, int fd)
7291{
7292 herefd = fd;
7293 expandarg(arg, (struct arglist *)NULL, 0);
7294 full_write(fd, stackblock(), expdest - (char *)stackblock());
7295}
7296
7297/*
7298 * Returns true if the pattern matches the string.
7299 */
7300static int
7301patmatch(char *pattern, const char *string)
7302{
7303 return pmatch(preglob(pattern, 0, 0), string);
7304}
7305
7306/*
7307 * See if a pattern matches in a case statement.
7308 */
7309static int
7310casematch(union node *pattern, char *val)
7311{
7312 struct stackmark smark;
7313 int result;
7314
7315 setstackmark(&smark);
7316 argbackq = pattern->narg.backquote;
7317 STARTSTACKSTR(expdest);
7318 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007319 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7320 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007321 STACKSTRNUL(expdest);
7322 result = patmatch(stackblock(), val);
7323 popstackmark(&smark);
7324 return result;
7325}
7326
7327
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007328/* ============ find_command */
7329
7330struct builtincmd {
7331 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007332 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333 /* unsigned flags; */
7334};
7335#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007336/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007337 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007338#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007339#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340
7341struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007342 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007343 union param {
7344 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007345 /* index >= 0 for commands without path (slashes) */
7346 /* (TODO: what exactly does the value mean? PATH position?) */
7347 /* index == -1 for commands with slashes */
7348 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007349 const struct builtincmd *cmd;
7350 struct funcnode *func;
7351 } u;
7352};
7353/* values of cmdtype */
7354#define CMDUNKNOWN -1 /* no entry in table for command */
7355#define CMDNORMAL 0 /* command is an executable program */
7356#define CMDFUNCTION 1 /* command is a shell function */
7357#define CMDBUILTIN 2 /* command is a shell builtin */
7358
7359/* action to find_command() */
7360#define DO_ERR 0x01 /* prints errors */
7361#define DO_ABS 0x02 /* checks absolute paths */
7362#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7363#define DO_ALTPATH 0x08 /* using alternate path */
7364#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7365
7366static void find_command(char *, struct cmdentry *, int, const char *);
7367
7368
7369/* ============ Hashing commands */
7370
7371/*
7372 * When commands are first encountered, they are entered in a hash table.
7373 * This ensures that a full path search will not have to be done for them
7374 * on each invocation.
7375 *
7376 * We should investigate converting to a linear search, even though that
7377 * would make the command name "hash" a misnomer.
7378 */
7379
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007380struct tblentry {
7381 struct tblentry *next; /* next entry in hash chain */
7382 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007383 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007384 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007385 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007386};
7387
Denis Vlasenko01631112007-12-16 17:20:38 +00007388static struct tblentry **cmdtable;
7389#define INIT_G_cmdtable() do { \
7390 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7391} while (0)
7392
7393static int builtinloc = -1; /* index in path of %builtin, or -1 */
7394
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007395
7396static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007397tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398{
7399 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007400
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007401#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007402 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007403 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007404 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007405 while (*envp)
7406 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007407 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007408 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007409 /* re-exec ourselves with the new arguments */
7410 execve(bb_busybox_exec_path, argv, envp);
7411 /* If they called chroot or otherwise made the binary no longer
7412 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007413 }
7414#endif
7415
7416 repeat:
7417#ifdef SYSV
7418 do {
7419 execve(cmd, argv, envp);
7420 } while (errno == EINTR);
7421#else
7422 execve(cmd, argv, envp);
7423#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007424 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007425 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007426 return;
7427 }
7428 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007429 char **ap;
7430 char **new;
7431
7432 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007433 continue;
7434 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007435 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007436 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007437 ap += 2;
7438 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007439 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007440 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007441 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007442 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007443 goto repeat;
7444 }
7445}
7446
7447/*
7448 * Exec a program. Never returns. If you change this routine, you may
7449 * have to change the find_command routine as well.
7450 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007451static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007452static void
7453shellexec(char **argv, const char *path, int idx)
7454{
7455 char *cmdname;
7456 int e;
7457 char **envp;
7458 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007459#if ENABLE_FEATURE_SH_STANDALONE
7460 int applet_no = -1;
7461#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007462
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007463 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007464 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007465 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007466#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007467 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007468#endif
7469 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007470 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007471 e = errno;
7472 } else {
7473 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007474 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007475 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007476 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007477 if (errno != ENOENT && errno != ENOTDIR)
7478 e = errno;
7479 }
7480 stunalloc(cmdname);
7481 }
7482 }
7483
7484 /* Map to POSIX errors */
7485 switch (e) {
7486 case EACCES:
7487 exerrno = 126;
7488 break;
7489 case ENOENT:
7490 exerrno = 127;
7491 break;
7492 default:
7493 exerrno = 2;
7494 break;
7495 }
7496 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007497 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7498 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007499 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7500 /* NOTREACHED */
7501}
7502
7503static void
7504printentry(struct tblentry *cmdp)
7505{
7506 int idx;
7507 const char *path;
7508 char *name;
7509
7510 idx = cmdp->param.index;
7511 path = pathval();
7512 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007513 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007514 stunalloc(name);
7515 } while (--idx >= 0);
7516 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7517}
7518
7519/*
7520 * Clear out command entries. The argument specifies the first entry in
7521 * PATH which has changed.
7522 */
7523static void
7524clearcmdentry(int firstchange)
7525{
7526 struct tblentry **tblp;
7527 struct tblentry **pp;
7528 struct tblentry *cmdp;
7529
7530 INT_OFF;
7531 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7532 pp = tblp;
7533 while ((cmdp = *pp) != NULL) {
7534 if ((cmdp->cmdtype == CMDNORMAL &&
7535 cmdp->param.index >= firstchange)
7536 || (cmdp->cmdtype == CMDBUILTIN &&
7537 builtinloc >= firstchange)
7538 ) {
7539 *pp = cmdp->next;
7540 free(cmdp);
7541 } else {
7542 pp = &cmdp->next;
7543 }
7544 }
7545 }
7546 INT_ON;
7547}
7548
7549/*
7550 * Locate a command in the command hash table. If "add" is nonzero,
7551 * add the command to the table if it is not already present. The
7552 * variable "lastcmdentry" is set to point to the address of the link
7553 * pointing to the entry, so that delete_cmd_entry can delete the
7554 * entry.
7555 *
7556 * Interrupts must be off if called with add != 0.
7557 */
7558static struct tblentry **lastcmdentry;
7559
7560static struct tblentry *
7561cmdlookup(const char *name, int add)
7562{
7563 unsigned int hashval;
7564 const char *p;
7565 struct tblentry *cmdp;
7566 struct tblentry **pp;
7567
7568 p = name;
7569 hashval = (unsigned char)*p << 4;
7570 while (*p)
7571 hashval += (unsigned char)*p++;
7572 hashval &= 0x7FFF;
7573 pp = &cmdtable[hashval % CMDTABLESIZE];
7574 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7575 if (strcmp(cmdp->cmdname, name) == 0)
7576 break;
7577 pp = &cmdp->next;
7578 }
7579 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007580 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7581 + strlen(name)
7582 /* + 1 - already done because
7583 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007584 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007585 cmdp->cmdtype = CMDUNKNOWN;
7586 strcpy(cmdp->cmdname, name);
7587 }
7588 lastcmdentry = pp;
7589 return cmdp;
7590}
7591
7592/*
7593 * Delete the command entry returned on the last lookup.
7594 */
7595static void
7596delete_cmd_entry(void)
7597{
7598 struct tblentry *cmdp;
7599
7600 INT_OFF;
7601 cmdp = *lastcmdentry;
7602 *lastcmdentry = cmdp->next;
7603 if (cmdp->cmdtype == CMDFUNCTION)
7604 freefunc(cmdp->param.func);
7605 free(cmdp);
7606 INT_ON;
7607}
7608
7609/*
7610 * Add a new command entry, replacing any existing command entry for
7611 * the same name - except special builtins.
7612 */
7613static void
7614addcmdentry(char *name, struct cmdentry *entry)
7615{
7616 struct tblentry *cmdp;
7617
7618 cmdp = cmdlookup(name, 1);
7619 if (cmdp->cmdtype == CMDFUNCTION) {
7620 freefunc(cmdp->param.func);
7621 }
7622 cmdp->cmdtype = entry->cmdtype;
7623 cmdp->param = entry->u;
7624 cmdp->rehash = 0;
7625}
7626
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007627static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007628hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007629{
7630 struct tblentry **pp;
7631 struct tblentry *cmdp;
7632 int c;
7633 struct cmdentry entry;
7634 char *name;
7635
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007636 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007637 clearcmdentry(0);
7638 return 0;
7639 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007640
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007641 if (*argptr == NULL) {
7642 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7643 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7644 if (cmdp->cmdtype == CMDNORMAL)
7645 printentry(cmdp);
7646 }
7647 }
7648 return 0;
7649 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007650
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007651 c = 0;
7652 while ((name = *argptr) != NULL) {
7653 cmdp = cmdlookup(name, 0);
7654 if (cmdp != NULL
7655 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007656 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7657 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007658 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007659 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007660 find_command(name, &entry, DO_ERR, pathval());
7661 if (entry.cmdtype == CMDUNKNOWN)
7662 c = 1;
7663 argptr++;
7664 }
7665 return c;
7666}
7667
7668/*
7669 * Called when a cd is done. Marks all commands so the next time they
7670 * are executed they will be rehashed.
7671 */
7672static void
7673hashcd(void)
7674{
7675 struct tblentry **pp;
7676 struct tblentry *cmdp;
7677
7678 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7679 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007680 if (cmdp->cmdtype == CMDNORMAL
7681 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007682 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007683 && builtinloc > 0)
7684 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007685 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007686 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007687 }
7688 }
7689}
7690
7691/*
7692 * Fix command hash table when PATH changed.
7693 * Called before PATH is changed. The argument is the new value of PATH;
7694 * pathval() still returns the old value at this point.
7695 * Called with interrupts off.
7696 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007697static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007698changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007699{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007700 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007701 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007702 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007703 int idx_bltin;
7704
7705 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007706 firstchange = 9999; /* assume no change */
7707 idx = 0;
7708 idx_bltin = -1;
7709 for (;;) {
7710 if (*old != *new) {
7711 firstchange = idx;
7712 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007713 || (*old == ':' && *new == '\0')
7714 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007715 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007716 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007717 old = new; /* ignore subsequent differences */
7718 }
7719 if (*new == '\0')
7720 break;
7721 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7722 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007723 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007724 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007725 new++;
7726 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007727 }
7728 if (builtinloc < 0 && idx_bltin >= 0)
7729 builtinloc = idx_bltin; /* zap builtins */
7730 if (builtinloc >= 0 && idx_bltin < 0)
7731 firstchange = 0;
7732 clearcmdentry(firstchange);
7733 builtinloc = idx_bltin;
7734}
7735
7736#define TEOF 0
7737#define TNL 1
7738#define TREDIR 2
7739#define TWORD 3
7740#define TSEMI 4
7741#define TBACKGND 5
7742#define TAND 6
7743#define TOR 7
7744#define TPIPE 8
7745#define TLP 9
7746#define TRP 10
7747#define TENDCASE 11
7748#define TENDBQUOTE 12
7749#define TNOT 13
7750#define TCASE 14
7751#define TDO 15
7752#define TDONE 16
7753#define TELIF 17
7754#define TELSE 18
7755#define TESAC 19
7756#define TFI 20
7757#define TFOR 21
7758#define TIF 22
7759#define TIN 23
7760#define TTHEN 24
7761#define TUNTIL 25
7762#define TWHILE 26
7763#define TBEGIN 27
7764#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007765typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007766
7767/* first char is indicating which tokens mark the end of a list */
7768static const char *const tokname_array[] = {
7769 "\1end of file",
7770 "\0newline",
7771 "\0redirection",
7772 "\0word",
7773 "\0;",
7774 "\0&",
7775 "\0&&",
7776 "\0||",
7777 "\0|",
7778 "\0(",
7779 "\1)",
7780 "\1;;",
7781 "\1`",
7782#define KWDOFFSET 13
7783 /* the following are keywords */
7784 "\0!",
7785 "\0case",
7786 "\1do",
7787 "\1done",
7788 "\1elif",
7789 "\1else",
7790 "\1esac",
7791 "\1fi",
7792 "\0for",
7793 "\0if",
7794 "\0in",
7795 "\1then",
7796 "\0until",
7797 "\0while",
7798 "\0{",
7799 "\1}",
7800};
7801
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007802/* Wrapper around strcmp for qsort/bsearch/... */
7803static int
7804pstrcmp(const void *a, const void *b)
7805{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007806 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007807}
7808
7809static const char *const *
7810findkwd(const char *s)
7811{
7812 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007813 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7814 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007815}
7816
7817/*
7818 * Locate and print what a word is...
7819 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007820static int
7821describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007822{
7823 struct cmdentry entry;
7824 struct tblentry *cmdp;
7825#if ENABLE_ASH_ALIAS
7826 const struct alias *ap;
7827#endif
7828 const char *path = pathval();
7829
7830 if (describe_command_verbose) {
7831 out1str(command);
7832 }
7833
7834 /* First look at the keywords */
7835 if (findkwd(command)) {
7836 out1str(describe_command_verbose ? " is a shell keyword" : command);
7837 goto out;
7838 }
7839
7840#if ENABLE_ASH_ALIAS
7841 /* Then look at the aliases */
7842 ap = lookupalias(command, 0);
7843 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007844 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007845 out1str("alias ");
7846 printalias(ap);
7847 return 0;
7848 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007849 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007850 goto out;
7851 }
7852#endif
7853 /* Then check if it is a tracked alias */
7854 cmdp = cmdlookup(command, 0);
7855 if (cmdp != NULL) {
7856 entry.cmdtype = cmdp->cmdtype;
7857 entry.u = cmdp->param;
7858 } else {
7859 /* Finally use brute force */
7860 find_command(command, &entry, DO_ABS, path);
7861 }
7862
7863 switch (entry.cmdtype) {
7864 case CMDNORMAL: {
7865 int j = entry.u.index;
7866 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007867 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007868 p = command;
7869 } else {
7870 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007871 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007872 stunalloc(p);
7873 } while (--j >= 0);
7874 }
7875 if (describe_command_verbose) {
7876 out1fmt(" is%s %s",
7877 (cmdp ? " a tracked alias for" : nullstr), p
7878 );
7879 } else {
7880 out1str(p);
7881 }
7882 break;
7883 }
7884
7885 case CMDFUNCTION:
7886 if (describe_command_verbose) {
7887 out1str(" is a shell function");
7888 } else {
7889 out1str(command);
7890 }
7891 break;
7892
7893 case CMDBUILTIN:
7894 if (describe_command_verbose) {
7895 out1fmt(" is a %sshell builtin",
7896 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7897 "special " : nullstr
7898 );
7899 } else {
7900 out1str(command);
7901 }
7902 break;
7903
7904 default:
7905 if (describe_command_verbose) {
7906 out1str(": not found\n");
7907 }
7908 return 127;
7909 }
7910 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007911 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007912 return 0;
7913}
7914
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007915static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007916typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007917{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007918 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007919 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007920 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921
Denis Vlasenko46846e22007-05-20 13:08:31 +00007922 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007923 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007924 i++;
7925 verbose = 0;
7926 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007927 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007928 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007929 }
7930 return err;
7931}
7932
7933#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007934static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007935commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007936{
7937 int c;
7938 enum {
7939 VERIFY_BRIEF = 1,
7940 VERIFY_VERBOSE = 2,
7941 } verify = 0;
7942
7943 while ((c = nextopt("pvV")) != '\0')
7944 if (c == 'V')
7945 verify |= VERIFY_VERBOSE;
7946 else if (c == 'v')
7947 verify |= VERIFY_BRIEF;
7948#if DEBUG
7949 else if (c != 'p')
7950 abort();
7951#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007952 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7953 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007954 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007955 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007956
7957 return 0;
7958}
7959#endif
7960
7961
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007962/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007963
Denis Vlasenko340299a2008-11-21 10:36:36 +00007964static int funcblocksize; /* size of structures in function */
7965static int funcstringsize; /* size of strings in node */
7966static void *funcblock; /* block to allocate function from */
7967static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007968
Eric Andersencb57d552001-06-28 07:25:16 +00007969/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007970#define EV_EXIT 01 /* exit after evaluating tree */
7971#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007972#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007973
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007974static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007975 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7976 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7977 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7978 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7979 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7980 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7981 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7982 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7983 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7984 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7985 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7986 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7987 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7988 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7989 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7990 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7991 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007992#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007993 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007994#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007995 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7996 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7997 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7998 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7999 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8000 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8001 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8002 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8003 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008004};
8005
8006static void calcsize(union node *n);
8007
8008static void
8009sizenodelist(struct nodelist *lp)
8010{
8011 while (lp) {
8012 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8013 calcsize(lp->n);
8014 lp = lp->next;
8015 }
8016}
8017
8018static void
8019calcsize(union node *n)
8020{
8021 if (n == NULL)
8022 return;
8023 funcblocksize += nodesize[n->type];
8024 switch (n->type) {
8025 case NCMD:
8026 calcsize(n->ncmd.redirect);
8027 calcsize(n->ncmd.args);
8028 calcsize(n->ncmd.assign);
8029 break;
8030 case NPIPE:
8031 sizenodelist(n->npipe.cmdlist);
8032 break;
8033 case NREDIR:
8034 case NBACKGND:
8035 case NSUBSHELL:
8036 calcsize(n->nredir.redirect);
8037 calcsize(n->nredir.n);
8038 break;
8039 case NAND:
8040 case NOR:
8041 case NSEMI:
8042 case NWHILE:
8043 case NUNTIL:
8044 calcsize(n->nbinary.ch2);
8045 calcsize(n->nbinary.ch1);
8046 break;
8047 case NIF:
8048 calcsize(n->nif.elsepart);
8049 calcsize(n->nif.ifpart);
8050 calcsize(n->nif.test);
8051 break;
8052 case NFOR:
8053 funcstringsize += strlen(n->nfor.var) + 1;
8054 calcsize(n->nfor.body);
8055 calcsize(n->nfor.args);
8056 break;
8057 case NCASE:
8058 calcsize(n->ncase.cases);
8059 calcsize(n->ncase.expr);
8060 break;
8061 case NCLIST:
8062 calcsize(n->nclist.body);
8063 calcsize(n->nclist.pattern);
8064 calcsize(n->nclist.next);
8065 break;
8066 case NDEFUN:
8067 case NARG:
8068 sizenodelist(n->narg.backquote);
8069 funcstringsize += strlen(n->narg.text) + 1;
8070 calcsize(n->narg.next);
8071 break;
8072 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008073#if ENABLE_ASH_BASH_COMPAT
8074 case NTO2:
8075#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008076 case NCLOBBER:
8077 case NFROM:
8078 case NFROMTO:
8079 case NAPPEND:
8080 calcsize(n->nfile.fname);
8081 calcsize(n->nfile.next);
8082 break;
8083 case NTOFD:
8084 case NFROMFD:
8085 calcsize(n->ndup.vname);
8086 calcsize(n->ndup.next);
8087 break;
8088 case NHERE:
8089 case NXHERE:
8090 calcsize(n->nhere.doc);
8091 calcsize(n->nhere.next);
8092 break;
8093 case NNOT:
8094 calcsize(n->nnot.com);
8095 break;
8096 };
8097}
8098
8099static char *
8100nodeckstrdup(char *s)
8101{
8102 char *rtn = funcstring;
8103
8104 strcpy(funcstring, s);
8105 funcstring += strlen(s) + 1;
8106 return rtn;
8107}
8108
8109static union node *copynode(union node *);
8110
8111static struct nodelist *
8112copynodelist(struct nodelist *lp)
8113{
8114 struct nodelist *start;
8115 struct nodelist **lpp;
8116
8117 lpp = &start;
8118 while (lp) {
8119 *lpp = funcblock;
8120 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8121 (*lpp)->n = copynode(lp->n);
8122 lp = lp->next;
8123 lpp = &(*lpp)->next;
8124 }
8125 *lpp = NULL;
8126 return start;
8127}
8128
8129static union node *
8130copynode(union node *n)
8131{
8132 union node *new;
8133
8134 if (n == NULL)
8135 return NULL;
8136 new = funcblock;
8137 funcblock = (char *) funcblock + nodesize[n->type];
8138
8139 switch (n->type) {
8140 case NCMD:
8141 new->ncmd.redirect = copynode(n->ncmd.redirect);
8142 new->ncmd.args = copynode(n->ncmd.args);
8143 new->ncmd.assign = copynode(n->ncmd.assign);
8144 break;
8145 case NPIPE:
8146 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008147 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008148 break;
8149 case NREDIR:
8150 case NBACKGND:
8151 case NSUBSHELL:
8152 new->nredir.redirect = copynode(n->nredir.redirect);
8153 new->nredir.n = copynode(n->nredir.n);
8154 break;
8155 case NAND:
8156 case NOR:
8157 case NSEMI:
8158 case NWHILE:
8159 case NUNTIL:
8160 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8161 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8162 break;
8163 case NIF:
8164 new->nif.elsepart = copynode(n->nif.elsepart);
8165 new->nif.ifpart = copynode(n->nif.ifpart);
8166 new->nif.test = copynode(n->nif.test);
8167 break;
8168 case NFOR:
8169 new->nfor.var = nodeckstrdup(n->nfor.var);
8170 new->nfor.body = copynode(n->nfor.body);
8171 new->nfor.args = copynode(n->nfor.args);
8172 break;
8173 case NCASE:
8174 new->ncase.cases = copynode(n->ncase.cases);
8175 new->ncase.expr = copynode(n->ncase.expr);
8176 break;
8177 case NCLIST:
8178 new->nclist.body = copynode(n->nclist.body);
8179 new->nclist.pattern = copynode(n->nclist.pattern);
8180 new->nclist.next = copynode(n->nclist.next);
8181 break;
8182 case NDEFUN:
8183 case NARG:
8184 new->narg.backquote = copynodelist(n->narg.backquote);
8185 new->narg.text = nodeckstrdup(n->narg.text);
8186 new->narg.next = copynode(n->narg.next);
8187 break;
8188 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008189#if ENABLE_ASH_BASH_COMPAT
8190 case NTO2:
8191#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192 case NCLOBBER:
8193 case NFROM:
8194 case NFROMTO:
8195 case NAPPEND:
8196 new->nfile.fname = copynode(n->nfile.fname);
8197 new->nfile.fd = n->nfile.fd;
8198 new->nfile.next = copynode(n->nfile.next);
8199 break;
8200 case NTOFD:
8201 case NFROMFD:
8202 new->ndup.vname = copynode(n->ndup.vname);
8203 new->ndup.dupfd = n->ndup.dupfd;
8204 new->ndup.fd = n->ndup.fd;
8205 new->ndup.next = copynode(n->ndup.next);
8206 break;
8207 case NHERE:
8208 case NXHERE:
8209 new->nhere.doc = copynode(n->nhere.doc);
8210 new->nhere.fd = n->nhere.fd;
8211 new->nhere.next = copynode(n->nhere.next);
8212 break;
8213 case NNOT:
8214 new->nnot.com = copynode(n->nnot.com);
8215 break;
8216 };
8217 new->type = n->type;
8218 return new;
8219}
8220
8221/*
8222 * Make a copy of a parse tree.
8223 */
8224static struct funcnode *
8225copyfunc(union node *n)
8226{
8227 struct funcnode *f;
8228 size_t blocksize;
8229
8230 funcblocksize = offsetof(struct funcnode, n);
8231 funcstringsize = 0;
8232 calcsize(n);
8233 blocksize = funcblocksize;
8234 f = ckmalloc(blocksize + funcstringsize);
8235 funcblock = (char *) f + offsetof(struct funcnode, n);
8236 funcstring = (char *) f + blocksize;
8237 copynode(n);
8238 f->count = 0;
8239 return f;
8240}
8241
8242/*
8243 * Define a shell function.
8244 */
8245static void
8246defun(char *name, union node *func)
8247{
8248 struct cmdentry entry;
8249
8250 INT_OFF;
8251 entry.cmdtype = CMDFUNCTION;
8252 entry.u.func = copyfunc(func);
8253 addcmdentry(name, &entry);
8254 INT_ON;
8255}
8256
Denis Vlasenko4b875702009-03-19 13:30:04 +00008257/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008258#define SKIPBREAK (1 << 0)
8259#define SKIPCONT (1 << 1)
8260#define SKIPFUNC (1 << 2)
8261#define SKIPFILE (1 << 3)
8262#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008263static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008264static int skipcount; /* number of levels to skip */
8265static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008266static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008267
Denis Vlasenko4b875702009-03-19 13:30:04 +00008268/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008269static int evalstring(char *s, int mask);
8270
Denis Vlasenko4b875702009-03-19 13:30:04 +00008271/* Called to execute a trap.
8272 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008273 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008274 *
8275 * Perhaps we should avoid entering new trap handlers
8276 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008277 */
8278static int
8279dotrap(void)
8280{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008281 uint8_t *g;
8282 int sig;
8283 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008284
8285 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008286 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008287 xbarrier();
8288
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008289 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008290 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8291 int want_exexit;
8292 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008293
Denis Vlasenko4b875702009-03-19 13:30:04 +00008294 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008295 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008296 t = trap[sig];
8297 /* non-trapped SIGINT is handled separately by raise_interrupt,
8298 * don't upset it by resetting gotsig[SIGINT-1] */
8299 if (sig == SIGINT && !t)
8300 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008301
8302 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008303 *g = 0;
8304 if (!t)
8305 continue;
8306 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008307 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008308 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008309 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008310 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008311 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008312 }
8313
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008314 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008315 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008316}
8317
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008318/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008319static void evalloop(union node *, int);
8320static void evalfor(union node *, int);
8321static void evalcase(union node *, int);
8322static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008323static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008324static void evalpipe(union node *, int);
8325static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008326static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008327static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008328
Eric Andersen62483552001-07-10 06:09:16 +00008329/*
Eric Andersenc470f442003-07-28 09:56:35 +00008330 * Evaluate a parse tree. The value is left in the global variable
8331 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008332 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008333static void
Eric Andersenc470f442003-07-28 09:56:35 +00008334evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008335{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008336 struct jmploc *volatile savehandler = exception_handler;
8337 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008338 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008339 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008340 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008341 int int_level;
8342
8343 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008344
Eric Andersenc470f442003-07-28 09:56:35 +00008345 if (n == NULL) {
8346 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008347 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008348 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008349 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008350
8351 exception_handler = &jmploc;
8352 {
8353 int err = setjmp(jmploc.loc);
8354 if (err) {
8355 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008356 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008357 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8358 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008359 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008360 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008361 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008362 TRACE(("exception %d in evaltree, propagating err=%d\n",
8363 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008364 exception_handler = savehandler;
8365 longjmp(exception_handler->loc, err);
8366 }
8367 }
8368
Eric Andersenc470f442003-07-28 09:56:35 +00008369 switch (n->type) {
8370 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008371#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008372 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008373 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008374 break;
8375#endif
8376 case NNOT:
8377 evaltree(n->nnot.com, EV_TESTED);
8378 status = !exitstatus;
8379 goto setstatus;
8380 case NREDIR:
8381 expredir(n->nredir.redirect);
8382 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8383 if (!status) {
8384 evaltree(n->nredir.n, flags & EV_TESTED);
8385 status = exitstatus;
8386 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008387 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008388 goto setstatus;
8389 case NCMD:
8390 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008391 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008392 if (eflag && !(flags & EV_TESTED))
8393 checkexit = ~0;
8394 goto calleval;
8395 case NFOR:
8396 evalfn = evalfor;
8397 goto calleval;
8398 case NWHILE:
8399 case NUNTIL:
8400 evalfn = evalloop;
8401 goto calleval;
8402 case NSUBSHELL:
8403 case NBACKGND:
8404 evalfn = evalsubshell;
8405 goto calleval;
8406 case NPIPE:
8407 evalfn = evalpipe;
8408 goto checkexit;
8409 case NCASE:
8410 evalfn = evalcase;
8411 goto calleval;
8412 case NAND:
8413 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008414 case NSEMI: {
8415
Eric Andersenc470f442003-07-28 09:56:35 +00008416#if NAND + 1 != NOR
8417#error NAND + 1 != NOR
8418#endif
8419#if NOR + 1 != NSEMI
8420#error NOR + 1 != NSEMI
8421#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008422 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008423 evaltree(
8424 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008425 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008426 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008427 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008428 break;
8429 if (!evalskip) {
8430 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008431 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008432 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008433 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008434 evalfn(n, flags);
8435 break;
8436 }
8437 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008438 }
Eric Andersenc470f442003-07-28 09:56:35 +00008439 case NIF:
8440 evaltree(n->nif.test, EV_TESTED);
8441 if (evalskip)
8442 break;
8443 if (exitstatus == 0) {
8444 n = n->nif.ifpart;
8445 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008446 }
8447 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008448 n = n->nif.elsepart;
8449 goto evaln;
8450 }
8451 goto success;
8452 case NDEFUN:
8453 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008454 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008455 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008456 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008457 exitstatus = status;
8458 break;
8459 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008460
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008461 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008462 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008463
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008464 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008465 /* Order of checks below is important:
8466 * signal handlers trigger before exit caused by "set -e".
8467 */
8468 if (pending_sig && dotrap())
8469 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008470 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008471 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008472
8473 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008474 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008475 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008476 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008477
8478 RESTORE_INT(int_level);
8479 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008480}
8481
Eric Andersenc470f442003-07-28 09:56:35 +00008482#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8483static
8484#endif
8485void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8486
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008487static void
Eric Andersenc470f442003-07-28 09:56:35 +00008488evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008489{
8490 int status;
8491
8492 loopnest++;
8493 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008494 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008495 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008496 int i;
8497
Eric Andersencb57d552001-06-28 07:25:16 +00008498 evaltree(n->nbinary.ch1, EV_TESTED);
8499 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008500 skipping:
8501 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008502 evalskip = 0;
8503 continue;
8504 }
8505 if (evalskip == SKIPBREAK && --skipcount <= 0)
8506 evalskip = 0;
8507 break;
8508 }
Eric Andersenc470f442003-07-28 09:56:35 +00008509 i = exitstatus;
8510 if (n->type != NWHILE)
8511 i = !i;
8512 if (i != 0)
8513 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008514 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008515 status = exitstatus;
8516 if (evalskip)
8517 goto skipping;
8518 }
8519 loopnest--;
8520 exitstatus = status;
8521}
8522
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008523static void
Eric Andersenc470f442003-07-28 09:56:35 +00008524evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008525{
8526 struct arglist arglist;
8527 union node *argp;
8528 struct strlist *sp;
8529 struct stackmark smark;
8530
8531 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008532 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008533 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008534 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008535 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008536 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008537 if (evalskip)
8538 goto out;
8539 }
8540 *arglist.lastp = NULL;
8541
8542 exitstatus = 0;
8543 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008544 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008545 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008546 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008547 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008548 if (evalskip) {
8549 if (evalskip == SKIPCONT && --skipcount <= 0) {
8550 evalskip = 0;
8551 continue;
8552 }
8553 if (evalskip == SKIPBREAK && --skipcount <= 0)
8554 evalskip = 0;
8555 break;
8556 }
8557 }
8558 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008559 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008560 popstackmark(&smark);
8561}
8562
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008563static void
Eric Andersenc470f442003-07-28 09:56:35 +00008564evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008565{
8566 union node *cp;
8567 union node *patp;
8568 struct arglist arglist;
8569 struct stackmark smark;
8570
8571 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008572 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008573 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008574 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008575 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008576 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8577 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008578 if (casematch(patp, arglist.list->text)) {
8579 if (evalskip == 0) {
8580 evaltree(cp->nclist.body, flags);
8581 }
8582 goto out;
8583 }
8584 }
8585 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008586 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008587 popstackmark(&smark);
8588}
8589
Eric Andersenc470f442003-07-28 09:56:35 +00008590/*
8591 * Kick off a subshell to evaluate a tree.
8592 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008593static void
Eric Andersenc470f442003-07-28 09:56:35 +00008594evalsubshell(union node *n, int flags)
8595{
8596 struct job *jp;
8597 int backgnd = (n->type == NBACKGND);
8598 int status;
8599
8600 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008601 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008602 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008603 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008604 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008605 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008606 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008607 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008608 flags |= EV_EXIT;
8609 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008610 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008611 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008612 redirect(n->nredir.redirect, 0);
8613 evaltreenr(n->nredir.n, flags);
8614 /* never returns */
8615 }
8616 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008617 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008618 status = waitforjob(jp);
8619 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008620 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008621}
8622
Eric Andersenc470f442003-07-28 09:56:35 +00008623/*
8624 * Compute the names of the files in a redirection list.
8625 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008626static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008627static void
8628expredir(union node *n)
8629{
8630 union node *redir;
8631
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008632 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008633 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008634
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008635 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008636 fn.lastp = &fn.list;
8637 switch (redir->type) {
8638 case NFROMTO:
8639 case NFROM:
8640 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008641#if ENABLE_ASH_BASH_COMPAT
8642 case NTO2:
8643#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008644 case NCLOBBER:
8645 case NAPPEND:
8646 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008647#if ENABLE_ASH_BASH_COMPAT
8648 store_expfname:
8649#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008650 redir->nfile.expfname = fn.list->text;
8651 break;
8652 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008653 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008654 if (redir->ndup.vname) {
8655 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008656 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008657 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008658#if ENABLE_ASH_BASH_COMPAT
8659//FIXME: we used expandarg with different args!
8660 if (!isdigit_str9(fn.list->text)) {
8661 /* >&file, not >&fd */
8662 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8663 ash_msg_and_raise_error("redir error");
8664 redir->type = NTO2;
8665 goto store_expfname;
8666 }
8667#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008668 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008669 }
8670 break;
8671 }
8672 }
8673}
8674
Eric Andersencb57d552001-06-28 07:25:16 +00008675/*
Eric Andersencb57d552001-06-28 07:25:16 +00008676 * Evaluate a pipeline. All the processes in the pipeline are children
8677 * of the process creating the pipeline. (This differs from some versions
8678 * of the shell, which make the last process in a pipeline the parent
8679 * of all the rest.)
8680 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008681static void
Eric Andersenc470f442003-07-28 09:56:35 +00008682evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008683{
8684 struct job *jp;
8685 struct nodelist *lp;
8686 int pipelen;
8687 int prevfd;
8688 int pip[2];
8689
Eric Andersenc470f442003-07-28 09:56:35 +00008690 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008691 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008692 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008693 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008694 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008695 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008696 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008697 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008698 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008699 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008700 pip[1] = -1;
8701 if (lp->next) {
8702 if (pipe(pip) < 0) {
8703 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008704 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008705 }
8706 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008707 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008708 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008709 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008710 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008711 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008712 if (prevfd > 0) {
8713 dup2(prevfd, 0);
8714 close(prevfd);
8715 }
8716 if (pip[1] > 1) {
8717 dup2(pip[1], 1);
8718 close(pip[1]);
8719 }
Eric Andersenc470f442003-07-28 09:56:35 +00008720 evaltreenr(lp->n, flags);
8721 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008722 }
8723 if (prevfd >= 0)
8724 close(prevfd);
8725 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008726 /* Don't want to trigger debugging */
8727 if (pip[1] != -1)
8728 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008729 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008730 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008731 exitstatus = waitforjob(jp);
8732 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008733 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008734 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008735}
8736
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008737/*
8738 * Controls whether the shell is interactive or not.
8739 */
8740static void
8741setinteractive(int on)
8742{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008743 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008744
8745 if (++on == is_interactive)
8746 return;
8747 is_interactive = on;
8748 setsignal(SIGINT);
8749 setsignal(SIGQUIT);
8750 setsignal(SIGTERM);
8751#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8752 if (is_interactive > 1) {
8753 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008754 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008755
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008756 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008757 /* note: ash and hush share this string */
8758 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008759 "Enter 'help' for a list of built-in commands."
8760 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008761 bb_banner,
8762 "built-in shell (ash)"
8763 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008764 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008765 }
8766 }
8767#endif
8768}
8769
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008770static void
8771optschanged(void)
8772{
8773#if DEBUG
8774 opentrace();
8775#endif
8776 setinteractive(iflag);
8777 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008778#if ENABLE_FEATURE_EDITING_VI
8779 if (viflag)
8780 line_input_state->flags |= VI_MODE;
8781 else
8782 line_input_state->flags &= ~VI_MODE;
8783#else
8784 viflag = 0; /* forcibly keep the option off */
8785#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008786}
8787
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008788static struct localvar *localvars;
8789
8790/*
8791 * Called after a function returns.
8792 * Interrupts must be off.
8793 */
8794static void
8795poplocalvars(void)
8796{
8797 struct localvar *lvp;
8798 struct var *vp;
8799
8800 while ((lvp = localvars) != NULL) {
8801 localvars = lvp->next;
8802 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008803 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008804 if (vp == NULL) { /* $- saved */
8805 memcpy(optlist, lvp->text, sizeof(optlist));
8806 free((char*)lvp->text);
8807 optschanged();
8808 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008809 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008810 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008811 if (vp->var_func)
8812 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008813 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008814 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008815 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008816 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008817 }
8818 free(lvp);
8819 }
8820}
8821
8822static int
8823evalfun(struct funcnode *func, int argc, char **argv, int flags)
8824{
8825 volatile struct shparam saveparam;
8826 struct localvar *volatile savelocalvars;
8827 struct jmploc *volatile savehandler;
8828 struct jmploc jmploc;
8829 int e;
8830
8831 saveparam = shellparam;
8832 savelocalvars = localvars;
8833 e = setjmp(jmploc.loc);
8834 if (e) {
8835 goto funcdone;
8836 }
8837 INT_OFF;
8838 savehandler = exception_handler;
8839 exception_handler = &jmploc;
8840 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008841 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008842 func->count++;
8843 funcnest++;
8844 INT_ON;
8845 shellparam.nparam = argc - 1;
8846 shellparam.p = argv + 1;
8847#if ENABLE_ASH_GETOPTS
8848 shellparam.optind = 1;
8849 shellparam.optoff = -1;
8850#endif
8851 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008852 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008853 INT_OFF;
8854 funcnest--;
8855 freefunc(func);
8856 poplocalvars();
8857 localvars = savelocalvars;
8858 freeparam(&shellparam);
8859 shellparam = saveparam;
8860 exception_handler = savehandler;
8861 INT_ON;
8862 evalskip &= ~SKIPFUNC;
8863 return e;
8864}
8865
Denis Vlasenko131ae172007-02-18 13:00:19 +00008866#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008867static char **
8868parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008869{
8870 char *cp, c;
8871
8872 for (;;) {
8873 cp = *++argv;
8874 if (!cp)
8875 return 0;
8876 if (*cp++ != '-')
8877 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008878 c = *cp++;
8879 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008880 break;
8881 if (c == '-' && !*cp) {
8882 argv++;
8883 break;
8884 }
8885 do {
8886 switch (c) {
8887 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008888 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008889 break;
8890 default:
8891 /* run 'typecmd' for other options */
8892 return 0;
8893 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008894 c = *cp++;
8895 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008896 }
8897 return argv;
8898}
8899#endif
8900
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008901/*
8902 * Make a variable a local variable. When a variable is made local, it's
8903 * value and flags are saved in a localvar structure. The saved values
8904 * will be restored when the shell function returns. We handle the name
8905 * "-" as a special case.
8906 */
8907static void
8908mklocal(char *name)
8909{
8910 struct localvar *lvp;
8911 struct var **vpp;
8912 struct var *vp;
8913
8914 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008915 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008916 if (LONE_DASH(name)) {
8917 char *p;
8918 p = ckmalloc(sizeof(optlist));
8919 lvp->text = memcpy(p, optlist, sizeof(optlist));
8920 vp = NULL;
8921 } else {
8922 char *eq;
8923
8924 vpp = hashvar(name);
8925 vp = *findvar(vpp, name);
8926 eq = strchr(name, '=');
8927 if (vp == NULL) {
8928 if (eq)
8929 setvareq(name, VSTRFIXED);
8930 else
8931 setvar(name, NULL, VSTRFIXED);
8932 vp = *vpp; /* the new variable */
8933 lvp->flags = VUNSET;
8934 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008935 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008936 lvp->flags = vp->flags;
8937 vp->flags |= VSTRFIXED|VTEXTFIXED;
8938 if (eq)
8939 setvareq(name, 0);
8940 }
8941 }
8942 lvp->vp = vp;
8943 lvp->next = localvars;
8944 localvars = lvp;
8945 INT_ON;
8946}
8947
8948/*
8949 * The "local" command.
8950 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008951static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008952localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008953{
8954 char *name;
8955
8956 argv = argptr;
8957 while ((name = *argv++) != NULL) {
8958 mklocal(name);
8959 }
8960 return 0;
8961}
8962
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008963static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008964falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008965{
8966 return 1;
8967}
8968
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008969static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008970truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008971{
8972 return 0;
8973}
8974
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008975static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008976execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008977{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008978 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008979 iflag = 0; /* exit on error */
8980 mflag = 0;
8981 optschanged();
8982 shellexec(argv + 1, pathval(), 0);
8983 }
8984 return 0;
8985}
8986
8987/*
8988 * The return command.
8989 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008990static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008991returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008992{
8993 /*
8994 * If called outside a function, do what ksh does;
8995 * skip the rest of the file.
8996 */
8997 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8998 return argv[1] ? number(argv[1]) : exitstatus;
8999}
9000
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009001/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009002static int breakcmd(int, char **) FAST_FUNC;
9003static int dotcmd(int, char **) FAST_FUNC;
9004static int evalcmd(int, char **) FAST_FUNC;
9005static int exitcmd(int, char **) FAST_FUNC;
9006static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009007#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009008static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009009#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009010#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009011static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009012#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009013#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009014static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009015#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009016static int readcmd(int, char **) FAST_FUNC;
9017static int setcmd(int, char **) FAST_FUNC;
9018static int shiftcmd(int, char **) FAST_FUNC;
9019static int timescmd(int, char **) FAST_FUNC;
9020static int trapcmd(int, char **) FAST_FUNC;
9021static int umaskcmd(int, char **) FAST_FUNC;
9022static int unsetcmd(int, char **) FAST_FUNC;
9023static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009024
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009025#define BUILTIN_NOSPEC "0"
9026#define BUILTIN_SPECIAL "1"
9027#define BUILTIN_REGULAR "2"
9028#define BUILTIN_SPEC_REG "3"
9029#define BUILTIN_ASSIGN "4"
9030#define BUILTIN_SPEC_ASSG "5"
9031#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009032#define BUILTIN_SPEC_REG_ASSG "7"
9033
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009034/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009035#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009036static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009037#endif
9038#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009039static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009040#endif
9041#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009042static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009043#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009044
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009045/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009046static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009047 { BUILTIN_SPEC_REG "." , dotcmd },
9048 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009049#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009050 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009051#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009053#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009054#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009056 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009057#endif
9058#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009059 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009060#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_SPEC_REG "break" , breakcmd },
9062 { BUILTIN_REGULAR "cd" , cdcmd },
9063 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009064#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009065 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009066#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009067 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009068#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009069 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_SPEC_REG "eval" , evalcmd },
9072 { BUILTIN_SPEC_REG "exec" , execcmd },
9073 { BUILTIN_SPEC_REG "exit" , exitcmd },
9074 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9075 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009076#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009077 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#endif
9079#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009080 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009081#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009083#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#endif
9086#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009087 { BUILTIN_REGULAR "jobs" , jobscmd },
9088 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009089#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009090#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009091 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009092#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009093 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009094#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009095 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009096#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009097 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9098 { BUILTIN_REGULAR "read" , readcmd },
9099 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9100 { BUILTIN_SPEC_REG "return" , returncmd },
9101 { BUILTIN_SPEC_REG "set" , setcmd },
9102 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009103#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009104 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009105#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009106#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009107 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009108#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009109 { BUILTIN_SPEC_REG "times" , timescmd },
9110 { BUILTIN_SPEC_REG "trap" , trapcmd },
9111 { BUILTIN_REGULAR "true" , truecmd },
9112 { BUILTIN_NOSPEC "type" , typecmd },
9113 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9114 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009115#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009116 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009117#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9119 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009120};
9121
Denis Vlasenko80591b02008-03-25 07:49:43 +00009122/* Should match the above table! */
9123#define COMMANDCMD (builtintab + \
9124 2 + \
9125 1 * ENABLE_ASH_BUILTIN_TEST + \
9126 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9127 1 * ENABLE_ASH_ALIAS + \
9128 1 * ENABLE_ASH_JOB_CONTROL + \
9129 3)
9130#define EXECCMD (builtintab + \
9131 2 + \
9132 1 * ENABLE_ASH_BUILTIN_TEST + \
9133 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9134 1 * ENABLE_ASH_ALIAS + \
9135 1 * ENABLE_ASH_JOB_CONTROL + \
9136 3 + \
9137 1 * ENABLE_ASH_CMDCMD + \
9138 1 + \
9139 ENABLE_ASH_BUILTIN_ECHO + \
9140 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009141
9142/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009143 * Search the table of builtin commands.
9144 */
9145static struct builtincmd *
9146find_builtin(const char *name)
9147{
9148 struct builtincmd *bp;
9149
9150 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009151 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009152 pstrcmp
9153 );
9154 return bp;
9155}
9156
9157/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009158 * Execute a simple command.
9159 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009160static int
9161isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009162{
9163 const char *q = endofname(p);
9164 if (p == q)
9165 return 0;
9166 return *q == '=';
9167}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009168static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009169bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009170{
9171 /* Preserve exitstatus of a previous possible redirection
9172 * as POSIX mandates */
9173 return back_exitstatus;
9174}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009175static void
Eric Andersenc470f442003-07-28 09:56:35 +00009176evalcommand(union node *cmd, int flags)
9177{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009178 static const struct builtincmd null_bltin = {
9179 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009180 };
Eric Andersenc470f442003-07-28 09:56:35 +00009181 struct stackmark smark;
9182 union node *argp;
9183 struct arglist arglist;
9184 struct arglist varlist;
9185 char **argv;
9186 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009187 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009188 struct cmdentry cmdentry;
9189 struct job *jp;
9190 char *lastarg;
9191 const char *path;
9192 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009193 int status;
9194 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009195 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009196 smallint cmd_is_exec;
9197 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009198
9199 /* First expand the arguments. */
9200 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9201 setstackmark(&smark);
9202 back_exitstatus = 0;
9203
9204 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009205 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009206 varlist.lastp = &varlist.list;
9207 *varlist.lastp = NULL;
9208 arglist.lastp = &arglist.list;
9209 *arglist.lastp = NULL;
9210
9211 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009212 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009213 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9214 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9215 }
9216
Eric Andersenc470f442003-07-28 09:56:35 +00009217 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9218 struct strlist **spp;
9219
9220 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009221 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009222 expandarg(argp, &arglist, EXP_VARTILDE);
9223 else
9224 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9225
Eric Andersenc470f442003-07-28 09:56:35 +00009226 for (sp = *spp; sp; sp = sp->next)
9227 argc++;
9228 }
9229
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009230 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009231 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009232 TRACE(("evalcommand arg: %s\n", sp->text));
9233 *nargv++ = sp->text;
9234 }
9235 *nargv = NULL;
9236
9237 lastarg = NULL;
9238 if (iflag && funcnest == 0 && argc > 0)
9239 lastarg = nargv[-1];
9240
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009241 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009242 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009243 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009244
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009245 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009246 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9247 struct strlist **spp;
9248 char *p;
9249
9250 spp = varlist.lastp;
9251 expandarg(argp, &varlist, EXP_VARTILDE);
9252
9253 /*
9254 * Modify the command lookup path, if a PATH= assignment
9255 * is present
9256 */
9257 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009258 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009259 path = p;
9260 }
9261
9262 /* Print the command if xflag is set. */
9263 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009264 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009265 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009266
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009267 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009268 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009269 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009270 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009271 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009272 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009273 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009274 }
9275 sp = arglist.list;
9276 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009277 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009278 }
9279
9280 cmd_is_exec = 0;
9281 spclbltin = -1;
9282
9283 /* Now locate the command. */
9284 if (argc) {
9285 const char *oldpath;
9286 int cmd_flag = DO_ERR;
9287
9288 path += 5;
9289 oldpath = path;
9290 for (;;) {
9291 find_command(argv[0], &cmdentry, cmd_flag, path);
9292 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009293 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009294 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009295 goto bail;
9296 }
9297
9298 /* implement bltin and command here */
9299 if (cmdentry.cmdtype != CMDBUILTIN)
9300 break;
9301 if (spclbltin < 0)
9302 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9303 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009304 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009305#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009306 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009307 path = oldpath;
9308 nargv = parse_command_args(argv, &path);
9309 if (!nargv)
9310 break;
9311 argc -= nargv - argv;
9312 argv = nargv;
9313 cmd_flag |= DO_NOFUNC;
9314 } else
9315#endif
9316 break;
9317 }
9318 }
9319
9320 if (status) {
9321 /* We have a redirection error. */
9322 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009323 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009324 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009325 exitstatus = status;
9326 goto out;
9327 }
9328
9329 /* Execute the command. */
9330 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009331 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009332
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009333#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009334/* (1) BUG: if variables are set, we need to fork, or save/restore them
9335 * around run_nofork_applet() call.
9336 * (2) Should this check also be done in forkshell()?
9337 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9338 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009339 /* find_command() encodes applet_no as (-2 - applet_no) */
9340 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009341 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009342 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009343 /* run <applet>_main() */
9344 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009345 break;
9346 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009347#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009348 /* Can we avoid forking off? For example, very last command
9349 * in a script or a subshell does not need forking,
9350 * we can just exec it.
9351 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009352 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009353 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009354 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009355 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009356 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009357 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009358 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009359 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009360 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009361 break;
9362 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009363 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009364 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009365 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009366 }
9367 listsetvar(varlist.list, VEXPORT|VSTACK);
9368 shellexec(argv, path, cmdentry.u.index);
9369 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009370 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009371 case CMDBUILTIN:
9372 cmdenviron = varlist.list;
9373 if (cmdenviron) {
9374 struct strlist *list = cmdenviron;
9375 int i = VNOSET;
9376 if (spclbltin > 0 || argc == 0) {
9377 i = 0;
9378 if (cmd_is_exec && argc > 1)
9379 i = VEXPORT;
9380 }
9381 listsetvar(list, i);
9382 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009383 /* Tight loop with builtins only:
9384 * "while kill -0 $child; do true; done"
9385 * will never exit even if $child died, unless we do this
9386 * to reap the zombie and make kill detect that it's gone: */
9387 dowait(DOWAIT_NONBLOCK, NULL);
9388
Eric Andersenc470f442003-07-28 09:56:35 +00009389 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9390 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009391 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009392 if (i == EXEXIT)
9393 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009394 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009395 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009396 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009397 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009398 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009399 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009400 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009401 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009402 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009403 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009404 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009405 }
9406 break;
9407
9408 case CMDFUNCTION:
9409 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009410 /* See above for the rationale */
9411 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009412 if (evalfun(cmdentry.u.func, argc, argv, flags))
9413 goto raise;
9414 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009415
9416 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009417
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009418 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009419 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009420 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009421 /* dsl: I think this is intended to be used to support
9422 * '_' in 'vi' command mode during line editing...
9423 * However I implemented that within libedit itself.
9424 */
9425 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009426 }
Eric Andersenc470f442003-07-28 09:56:35 +00009427 popstackmark(&smark);
9428}
9429
9430static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009431evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9432{
Eric Andersenc470f442003-07-28 09:56:35 +00009433 char *volatile savecmdname;
9434 struct jmploc *volatile savehandler;
9435 struct jmploc jmploc;
9436 int i;
9437
9438 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009439 i = setjmp(jmploc.loc);
9440 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009441 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009442 savehandler = exception_handler;
9443 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009444 commandname = argv[0];
9445 argptr = argv + 1;
9446 optptr = NULL; /* initialize nextopt */
9447 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009448 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009449 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009450 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009451 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009452 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009453 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009454
9455 return i;
9456}
9457
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009458static int
9459goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009460{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009461 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009462}
9463
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009464
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009465/*
9466 * Search for a command. This is called before we fork so that the
9467 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009468 * the child. The check for "goodname" is an overly conservative
9469 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009470 */
Eric Andersenc470f442003-07-28 09:56:35 +00009471static void
9472prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009473{
9474 struct cmdentry entry;
9475
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009476 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9477 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009478}
9479
Eric Andersencb57d552001-06-28 07:25:16 +00009480
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009481/* ============ Builtin commands
9482 *
9483 * Builtin commands whose functions are closely tied to evaluation
9484 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009485 */
9486
9487/*
Eric Andersencb57d552001-06-28 07:25:16 +00009488 * Handle break and continue commands. Break, continue, and return are
9489 * all handled by setting the evalskip flag. The evaluation routines
9490 * above all check this flag, and if it is set they start skipping
9491 * commands rather than executing them. The variable skipcount is
9492 * the number of loops to break/continue, or the number of function
9493 * levels to return. (The latter is always 1.) It should probably
9494 * be an error to break out of more loops than exist, but it isn't
9495 * in the standard shell so we don't make it one here.
9496 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009497static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009498breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009499{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009500 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009501
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009502 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009503 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009504 if (n > loopnest)
9505 n = loopnest;
9506 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009507 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009508 skipcount = n;
9509 }
9510 return 0;
9511}
9512
Eric Andersenc470f442003-07-28 09:56:35 +00009513
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009514/* ============ input.c
9515 *
Eric Andersen90898442003-08-06 11:20:52 +00009516 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009517 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009518
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009519enum {
9520 INPUT_PUSH_FILE = 1,
9521 INPUT_NOFILE_OK = 2,
9522};
Eric Andersencb57d552001-06-28 07:25:16 +00009523
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009524static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009525/* values of checkkwd variable */
9526#define CHKALIAS 0x1
9527#define CHKKWD 0x2
9528#define CHKNL 0x4
9529
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009530/*
9531 * Push a string back onto the input at this current parsefile level.
9532 * We handle aliases this way.
9533 */
9534#if !ENABLE_ASH_ALIAS
9535#define pushstring(s, ap) pushstring(s)
9536#endif
9537static void
9538pushstring(char *s, struct alias *ap)
9539{
9540 struct strpush *sp;
9541 int len;
9542
9543 len = strlen(s);
9544 INT_OFF;
9545 if (g_parsefile->strpush) {
9546 sp = ckzalloc(sizeof(*sp));
9547 sp->prev = g_parsefile->strpush;
9548 } else {
9549 sp = &(g_parsefile->basestrpush);
9550 }
9551 g_parsefile->strpush = sp;
9552 sp->prev_string = g_parsefile->next_to_pgetc;
9553 sp->prev_left_in_line = g_parsefile->left_in_line;
9554#if ENABLE_ASH_ALIAS
9555 sp->ap = ap;
9556 if (ap) {
9557 ap->flag |= ALIASINUSE;
9558 sp->string = s;
9559 }
9560#endif
9561 g_parsefile->next_to_pgetc = s;
9562 g_parsefile->left_in_line = len;
9563 INT_ON;
9564}
9565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009566static void
9567popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009568{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009569 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009570
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009571 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009572#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009573 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009574 if (g_parsefile->next_to_pgetc[-1] == ' '
9575 || g_parsefile->next_to_pgetc[-1] == '\t'
9576 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009577 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009578 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009579 if (sp->string != sp->ap->val) {
9580 free(sp->string);
9581 }
9582 sp->ap->flag &= ~ALIASINUSE;
9583 if (sp->ap->flag & ALIASDEAD) {
9584 unalias(sp->ap->name);
9585 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009586 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009587#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009588 g_parsefile->next_to_pgetc = sp->prev_string;
9589 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009590 g_parsefile->strpush = sp->prev;
9591 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009592 free(sp);
9593 INT_ON;
9594}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009595
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009596//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9597//it peeks whether it is &>, and then pushes back both chars.
9598//This function needs to save last *next_to_pgetc to buf[0]
9599//to make two pungetc() reliable. Currently,
9600// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009601static int
9602preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009603{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009604 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009605 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009606
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009607 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009608#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009609 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009610 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9611 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009612 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009613 int timeout = -1;
9614# if ENABLE_ASH_IDLE_TIMEOUT
9615 if (iflag) {
9616 const char *tmout_var = lookupvar("TMOUT");
9617 if (tmout_var) {
9618 timeout = atoi(tmout_var) * 1000;
9619 if (timeout <= 0)
9620 timeout = -1;
9621 }
9622 }
9623# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009624# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009625 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009626# endif
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009627 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009628 if (nr == 0) {
9629 /* Ctrl+C pressed */
9630 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009631 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009632 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009633 raise(SIGINT);
9634 return 1;
9635 }
Eric Andersenc470f442003-07-28 09:56:35 +00009636 goto retry;
9637 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009638 if (nr < 0) {
9639 if (errno == 0) {
9640 /* Ctrl+D pressed */
9641 nr = 0;
9642 }
9643# if ENABLE_ASH_IDLE_TIMEOUT
9644 else if (errno == EAGAIN && timeout > 0) {
9645 printf("\007timed out waiting for input: auto-logout\n");
9646 exitshell();
9647 }
9648# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009649 }
Eric Andersencb57d552001-06-28 07:25:16 +00009650 }
9651#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009652 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009653#endif
9654
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009655#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009656 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009657 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009658 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009659 if (flags >= 0 && (flags & O_NONBLOCK)) {
9660 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009661 if (fcntl(0, F_SETFL, flags) >= 0) {
9662 out2str("sh: turning off NDELAY mode\n");
9663 goto retry;
9664 }
9665 }
9666 }
9667 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009668#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009669 return nr;
9670}
9671
9672/*
9673 * Refill the input buffer and return the next input character:
9674 *
9675 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009676 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9677 * or we are reading from a string so we can't refill the buffer,
9678 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009679 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009680 * 4) Process input up to the next newline, deleting nul characters.
9681 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009682//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9683#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009684static int
Eric Andersenc470f442003-07-28 09:56:35 +00009685preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009686{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009687 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009688 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009689
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009690 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009691#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009692 if (g_parsefile->left_in_line == -1
9693 && g_parsefile->strpush->ap
9694 && g_parsefile->next_to_pgetc[-1] != ' '
9695 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009696 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009697 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009698 return PEOA;
9699 }
Eric Andersen2870d962001-07-02 17:27:21 +00009700#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009701 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009702 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009703 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9704 g_parsefile->left_in_line,
9705 g_parsefile->next_to_pgetc,
9706 g_parsefile->next_to_pgetc);
9707 if (--g_parsefile->left_in_line >= 0)
9708 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009709 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009710 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009711 * "pgetc" needs refilling.
9712 */
9713
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009714 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009715 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009716 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009717 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009718 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009719 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009720 /* even in failure keep left_in_line and next_to_pgetc
9721 * in lock step, for correct multi-layer pungetc.
9722 * left_in_line was decremented before preadbuffer(),
9723 * must inc next_to_pgetc: */
9724 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009725 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009726 }
Eric Andersencb57d552001-06-28 07:25:16 +00009727
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009728 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009729 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009730 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009731 again:
9732 more = preadfd();
9733 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009734 /* don't try reading again */
9735 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009736 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009737 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009738 return PEOF;
9739 }
9740 }
9741
Denis Vlasenko727752d2008-11-28 03:41:47 +00009742 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009743 * Set g_parsefile->left_in_line
9744 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009745 * NUL chars are deleted.
9746 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009747 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009748 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009749 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009750
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009751 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009752
Denis Vlasenko727752d2008-11-28 03:41:47 +00009753 c = *q;
9754 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009755 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009756 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 q++;
9758 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009759 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009760 break;
9761 }
Eric Andersencb57d552001-06-28 07:25:16 +00009762 }
9763
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009764 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009765 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9766 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009767 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009768 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009769 }
9770 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009771 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009772
Eric Andersencb57d552001-06-28 07:25:16 +00009773 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009774 char save = *q;
9775 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009776 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009777 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009778 }
9779
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009780 pgetc_debug("preadbuffer at %d:%p'%s'",
9781 g_parsefile->left_in_line,
9782 g_parsefile->next_to_pgetc,
9783 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009784 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009785}
9786
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009787#define pgetc_as_macro() \
9788 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009789 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009790 : preadbuffer() \
9791 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009792
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009793static int
9794pgetc(void)
9795{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009796 pgetc_debug("pgetc_fast at %d:%p'%s'",
9797 g_parsefile->left_in_line,
9798 g_parsefile->next_to_pgetc,
9799 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009800 return pgetc_as_macro();
9801}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009802
9803#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009804# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009805#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009806# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009807#endif
9808
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809#if ENABLE_ASH_ALIAS
9810static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009811pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009812{
9813 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009815 pgetc_debug("pgetc_fast at %d:%p'%s'",
9816 g_parsefile->left_in_line,
9817 g_parsefile->next_to_pgetc,
9818 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009819 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009820 } while (c == PEOA);
9821 return c;
9822}
9823#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009824# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009825#endif
9826
9827/*
9828 * Read a line from the script.
9829 */
9830static char *
9831pfgets(char *line, int len)
9832{
9833 char *p = line;
9834 int nleft = len;
9835 int c;
9836
9837 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009838 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009839 if (c == PEOF) {
9840 if (p == line)
9841 return NULL;
9842 break;
9843 }
9844 *p++ = c;
9845 if (c == '\n')
9846 break;
9847 }
9848 *p = '\0';
9849 return line;
9850}
9851
Eric Andersenc470f442003-07-28 09:56:35 +00009852/*
9853 * Undo the last call to pgetc. Only one character may be pushed back.
9854 * PEOF may be pushed back.
9855 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009856static void
Eric Andersenc470f442003-07-28 09:56:35 +00009857pungetc(void)
9858{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009859 g_parsefile->left_in_line++;
9860 g_parsefile->next_to_pgetc--;
9861 pgetc_debug("pushed back to %d:%p'%s'",
9862 g_parsefile->left_in_line,
9863 g_parsefile->next_to_pgetc,
9864 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009865}
9866
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009867/*
9868 * To handle the "." command, a stack of input files is used. Pushfile
9869 * adds a new entry to the stack and popfile restores the previous level.
9870 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009871static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009872pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009873{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009874 struct parsefile *pf;
9875
Denis Vlasenko597906c2008-02-20 16:38:54 +00009876 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009877 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009878 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009879 /*pf->strpush = NULL; - ckzalloc did it */
9880 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009881 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009882}
9883
9884static void
9885popfile(void)
9886{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009887 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009888
Denis Vlasenkob012b102007-02-19 22:43:01 +00009889 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009890 if (pf->pf_fd >= 0)
9891 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009892 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009893 while (pf->strpush)
9894 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009895 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009896 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009897 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009898}
9899
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009900/*
9901 * Return to top level.
9902 */
9903static void
9904popallfiles(void)
9905{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009906 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009907 popfile();
9908}
9909
9910/*
9911 * Close the file(s) that the shell is reading commands from. Called
9912 * after a fork is done.
9913 */
9914static void
9915closescript(void)
9916{
9917 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009918 if (g_parsefile->pf_fd > 0) {
9919 close(g_parsefile->pf_fd);
9920 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009921 }
9922}
9923
9924/*
9925 * Like setinputfile, but takes an open file descriptor. Call this with
9926 * interrupts off.
9927 */
9928static void
9929setinputfd(int fd, int push)
9930{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009931 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009932 if (push) {
9933 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009934 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009935 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009936 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009937 if (g_parsefile->buf == NULL)
9938 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009939 g_parsefile->left_in_buffer = 0;
9940 g_parsefile->left_in_line = 0;
9941 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009942}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009943
Eric Andersenc470f442003-07-28 09:56:35 +00009944/*
9945 * Set the input to take input from a file. If push is set, push the
9946 * old input onto the stack first.
9947 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009948static int
9949setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009950{
9951 int fd;
9952 int fd2;
9953
Denis Vlasenkob012b102007-02-19 22:43:01 +00009954 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009955 fd = open(fname, O_RDONLY);
9956 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009957 if (flags & INPUT_NOFILE_OK)
9958 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009959 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009960 }
Eric Andersenc470f442003-07-28 09:56:35 +00009961 if (fd < 10) {
9962 fd2 = copyfd(fd, 10);
9963 close(fd);
9964 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009965 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009966 fd = fd2;
9967 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009968 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009969 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009970 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009971 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009972}
9973
Eric Andersencb57d552001-06-28 07:25:16 +00009974/*
9975 * Like setinputfile, but takes input from a string.
9976 */
Eric Andersenc470f442003-07-28 09:56:35 +00009977static void
9978setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009979{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009980 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009981 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009982 g_parsefile->next_to_pgetc = string;
9983 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009984 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009985 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009986 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009987}
9988
9989
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009990/* ============ mail.c
9991 *
9992 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009993 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009994
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009995#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009996
Eric Andersencb57d552001-06-28 07:25:16 +00009997#define MAXMBOXES 10
9998
Eric Andersenc470f442003-07-28 09:56:35 +00009999/* times of mailboxes */
10000static time_t mailtime[MAXMBOXES];
10001/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010002static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010003
Eric Andersencb57d552001-06-28 07:25:16 +000010004/*
Eric Andersenc470f442003-07-28 09:56:35 +000010005 * Print appropriate message(s) if mail has arrived.
10006 * If mail_var_path_changed is set,
10007 * then the value of MAIL has mail_var_path_changed,
10008 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010009 */
Eric Andersenc470f442003-07-28 09:56:35 +000010010static void
10011chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010012{
Eric Andersencb57d552001-06-28 07:25:16 +000010013 const char *mpath;
10014 char *p;
10015 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010016 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010017 struct stackmark smark;
10018 struct stat statb;
10019
Eric Andersencb57d552001-06-28 07:25:16 +000010020 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010021 mpath = mpathset() ? mpathval() : mailval();
10022 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010023 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010024 if (p == NULL)
10025 break;
10026 if (*p == '\0')
10027 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010028 for (q = p; *q; q++)
10029 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010030#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010031 if (q[-1] != '/')
10032 abort();
10033#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010034 q[-1] = '\0'; /* delete trailing '/' */
10035 if (stat(p, &statb) < 0) {
10036 *mtp = 0;
10037 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010038 }
Eric Andersenc470f442003-07-28 09:56:35 +000010039 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10040 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010041 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010042 pathopt ? pathopt : "you have mail"
10043 );
10044 }
10045 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010046 }
Eric Andersenc470f442003-07-28 09:56:35 +000010047 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010048 popstackmark(&smark);
10049}
Eric Andersencb57d552001-06-28 07:25:16 +000010050
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010051static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010052changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010053{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010054 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010055}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010056
Denis Vlasenko131ae172007-02-18 13:00:19 +000010057#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010058
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010059
10060/* ============ ??? */
10061
Eric Andersencb57d552001-06-28 07:25:16 +000010062/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010063 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010064 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010065static void
10066setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010067{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010068 char **newparam;
10069 char **ap;
10070 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010071
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010072 for (nparam = 0; argv[nparam]; nparam++)
10073 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010074 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10075 while (*argv) {
10076 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010077 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010078 *ap = NULL;
10079 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010080 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010081 shellparam.nparam = nparam;
10082 shellparam.p = newparam;
10083#if ENABLE_ASH_GETOPTS
10084 shellparam.optind = 1;
10085 shellparam.optoff = -1;
10086#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010087}
10088
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010089/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010090 * Process shell options. The global variable argptr contains a pointer
10091 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010092 *
10093 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10094 * For a non-interactive shell, an error condition encountered
10095 * by a special built-in ... shall cause the shell to write a diagnostic message
10096 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010097 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010098 * ...
10099 * Utility syntax error (option or operand error) Shall exit
10100 * ...
10101 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10102 * we see that bash does not do that (set "finishes" with error code 1 instead,
10103 * and shell continues), and people rely on this behavior!
10104 * Testcase:
10105 * set -o barfoo 2>/dev/null
10106 * echo $?
10107 *
10108 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010109 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010110static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010111plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010112{
10113 int i;
10114
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010115 if (name) {
10116 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010117 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010118 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010119 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010120 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010121 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010122 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010123 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010124 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010125 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010126 if (val) {
10127 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10128 } else {
10129 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10130 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010131 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010132 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010133}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010134static void
10135setoption(int flag, int val)
10136{
10137 int i;
10138
10139 for (i = 0; i < NOPTS; i++) {
10140 if (optletters(i) == flag) {
10141 optlist[i] = val;
10142 return;
10143 }
10144 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010145 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010146 /* NOTREACHED */
10147}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010148static int
Eric Andersenc470f442003-07-28 09:56:35 +000010149options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010150{
10151 char *p;
10152 int val;
10153 int c;
10154
10155 if (cmdline)
10156 minusc = NULL;
10157 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010158 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010159 if (c != '-' && c != '+')
10160 break;
10161 argptr++;
10162 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010163 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010164 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010165 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010166 if (!cmdline) {
10167 /* "-" means turn off -x and -v */
10168 if (p[0] == '\0')
10169 xflag = vflag = 0;
10170 /* "--" means reset params */
10171 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010172 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010173 }
Eric Andersenc470f442003-07-28 09:56:35 +000010174 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010175 }
Eric Andersencb57d552001-06-28 07:25:16 +000010176 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010177 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010178 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010179 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010180 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010181 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010182 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010183 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010184 /* it already printed err message */
10185 return 1; /* error */
10186 }
Eric Andersencb57d552001-06-28 07:25:16 +000010187 if (*argptr)
10188 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010189 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10190 isloginsh = 1;
10191 /* bash does not accept +-login, we also won't */
10192 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010193 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010194 isloginsh = 1;
10195 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010196 } else {
10197 setoption(c, val);
10198 }
10199 }
10200 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010201 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010202}
10203
Eric Andersencb57d552001-06-28 07:25:16 +000010204/*
Eric Andersencb57d552001-06-28 07:25:16 +000010205 * The shift builtin command.
10206 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010207static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010208shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010209{
10210 int n;
10211 char **ap1, **ap2;
10212
10213 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010214 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010215 n = number(argv[1]);
10216 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010217 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010218 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010219 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010220 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010221 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010222 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010223 }
10224 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010225 while ((*ap2++ = *ap1++) != NULL)
10226 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010227#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010228 shellparam.optind = 1;
10229 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010230#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010231 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010232 return 0;
10233}
10234
Eric Andersencb57d552001-06-28 07:25:16 +000010235/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010236 * POSIX requires that 'set' (but not export or readonly) output the
10237 * variables in lexicographic order - by the locale's collating order (sigh).
10238 * Maybe we could keep them in an ordered balanced binary tree
10239 * instead of hashed lists.
10240 * For now just roll 'em through qsort for printing...
10241 */
10242static int
10243showvars(const char *sep_prefix, int on, int off)
10244{
10245 const char *sep;
10246 char **ep, **epend;
10247
10248 ep = listvars(on, off, &epend);
10249 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10250
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010251 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010252
10253 for (; ep < epend; ep++) {
10254 const char *p;
10255 const char *q;
10256
10257 p = strchrnul(*ep, '=');
10258 q = nullstr;
10259 if (*p)
10260 q = single_quote(++p);
10261 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10262 }
10263 return 0;
10264}
10265
10266/*
Eric Andersencb57d552001-06-28 07:25:16 +000010267 * The set command builtin.
10268 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010269static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010270setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010271{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010272 int retval;
10273
Denis Vlasenko68404f12008-03-17 09:00:54 +000010274 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010275 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010276 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010277 retval = 1;
10278 if (!options(0)) { /* if no parse error... */
10279 retval = 0;
10280 optschanged();
10281 if (*argptr != NULL) {
10282 setparam(argptr);
10283 }
Eric Andersencb57d552001-06-28 07:25:16 +000010284 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010285 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010286 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010287}
10288
Denis Vlasenko131ae172007-02-18 13:00:19 +000010289#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010290static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010291change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010292{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010293 uint32_t t;
10294
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010295 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010296 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010297 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010298 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010299 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010300 vrandom.flags &= ~VNOFUNC;
10301 } else {
10302 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010303 t = strtoul(value, NULL, 10);
10304 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010305 }
Eric Andersenef02f822004-03-11 13:34:24 +000010306}
Eric Andersen16767e22004-03-16 05:14:10 +000010307#endif
10308
Denis Vlasenko131ae172007-02-18 13:00:19 +000010309#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010310static int
Eric Andersenc470f442003-07-28 09:56:35 +000010311getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010312{
10313 char *p, *q;
10314 char c = '?';
10315 int done = 0;
10316 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010317 char s[12];
10318 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010319
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010320 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010321 return 1;
10322 optnext = optfirst + *param_optind - 1;
10323
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010324 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010325 p = NULL;
10326 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010327 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010328 if (p == NULL || *p == '\0') {
10329 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010330 p = *optnext;
10331 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010332 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010333 p = NULL;
10334 done = 1;
10335 goto out;
10336 }
10337 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010338 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010339 goto atend;
10340 }
10341
10342 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010343 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010344 if (*q == '\0') {
10345 if (optstr[0] == ':') {
10346 s[0] = c;
10347 s[1] = '\0';
10348 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010349 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010350 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010351 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010352 }
10353 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010354 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010355 }
10356 if (*++q == ':')
10357 q++;
10358 }
10359
10360 if (*++q == ':') {
10361 if (*p == '\0' && (p = *optnext) == NULL) {
10362 if (optstr[0] == ':') {
10363 s[0] = c;
10364 s[1] = '\0';
10365 err |= setvarsafe("OPTARG", s, 0);
10366 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010367 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010368 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010369 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010370 c = '?';
10371 }
Eric Andersenc470f442003-07-28 09:56:35 +000010372 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010373 }
10374
10375 if (p == *optnext)
10376 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010377 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010378 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010379 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010380 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010381 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010382 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010383 *param_optind = optnext - optfirst + 1;
10384 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010385 err |= setvarsafe("OPTIND", s, VNOFUNC);
10386 s[0] = c;
10387 s[1] = '\0';
10388 err |= setvarsafe(optvar, s, 0);
10389 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010390 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010392 flush_stdout_stderr();
10393 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010394 }
10395 return done;
10396}
Eric Andersenc470f442003-07-28 09:56:35 +000010397
10398/*
10399 * The getopts builtin. Shellparam.optnext points to the next argument
10400 * to be processed. Shellparam.optptr points to the next character to
10401 * be processed in the current argument. If shellparam.optnext is NULL,
10402 * then it's the first time getopts has been called.
10403 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010404static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010405getoptscmd(int argc, char **argv)
10406{
10407 char **optbase;
10408
10409 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010410 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010411 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010412 optbase = shellparam.p;
10413 if (shellparam.optind > shellparam.nparam + 1) {
10414 shellparam.optind = 1;
10415 shellparam.optoff = -1;
10416 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010417 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010418 optbase = &argv[3];
10419 if (shellparam.optind > argc - 2) {
10420 shellparam.optind = 1;
10421 shellparam.optoff = -1;
10422 }
10423 }
10424
10425 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010426 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010427}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010428#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010429
Eric Andersencb57d552001-06-28 07:25:16 +000010430
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010431/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010432
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010433struct heredoc {
10434 struct heredoc *next; /* next here document in list */
10435 union node *here; /* redirection node */
10436 char *eofmark; /* string indicating end of input */
10437 smallint striptabs; /* if set, strip leading tabs */
10438};
10439
10440static smallint tokpushback; /* last token pushed back */
10441static smallint parsebackquote; /* nonzero if we are inside backquotes */
10442static smallint quoteflag; /* set if (part of) last token was quoted */
10443static token_id_t lasttoken; /* last token read (integer id Txxx) */
10444static struct heredoc *heredoclist; /* list of here documents to read */
10445static char *wordtext; /* text of last word returned by readtoken */
10446static struct nodelist *backquotelist;
10447static union node *redirnode;
10448static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010449
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010450static const char *
10451tokname(char *buf, int tok)
10452{
10453 if (tok < TSEMI)
10454 return tokname_array[tok] + 1;
10455 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10456 return buf;
10457}
10458
10459/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010460 * Called when an unexpected token is read during the parse. The argument
10461 * is the token that is expected, or -1 if more than one type of token can
10462 * occur at this point.
10463 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010464static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010465static void
10466raise_error_unexpected_syntax(int token)
10467{
10468 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010469 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010470 int l;
10471
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010472 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010473 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010474 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010475 raise_error_syntax(msg);
10476 /* NOTREACHED */
10477}
Eric Andersencb57d552001-06-28 07:25:16 +000010478
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010479#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010480
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010481/* parsing is heavily cross-recursive, need these forward decls */
10482static union node *andor(void);
10483static union node *pipeline(void);
10484static union node *parse_command(void);
10485static void parseheredoc(void);
10486static char peektoken(void);
10487static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010488
Eric Andersenc470f442003-07-28 09:56:35 +000010489static union node *
10490list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010491{
10492 union node *n1, *n2, *n3;
10493 int tok;
10494
Eric Andersenc470f442003-07-28 09:56:35 +000010495 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10496 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010497 return NULL;
10498 n1 = NULL;
10499 for (;;) {
10500 n2 = andor();
10501 tok = readtoken();
10502 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010503 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010504 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010505 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010506 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010507 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010508 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010509 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010510 n2 = n3;
10511 }
10512 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010513 }
10514 }
10515 if (n1 == NULL) {
10516 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010517 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010518 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010519 n3->type = NSEMI;
10520 n3->nbinary.ch1 = n1;
10521 n3->nbinary.ch2 = n2;
10522 n1 = n3;
10523 }
10524 switch (tok) {
10525 case TBACKGND:
10526 case TSEMI:
10527 tok = readtoken();
10528 /* fall through */
10529 case TNL:
10530 if (tok == TNL) {
10531 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010532 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010533 return n1;
10534 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010535 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010536 }
Eric Andersenc470f442003-07-28 09:56:35 +000010537 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010538 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010539 return n1;
10540 break;
10541 case TEOF:
10542 if (heredoclist)
10543 parseheredoc();
10544 else
Eric Andersenc470f442003-07-28 09:56:35 +000010545 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010546 return n1;
10547 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010548 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010549 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010550 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010551 return n1;
10552 }
10553 }
10554}
10555
Eric Andersenc470f442003-07-28 09:56:35 +000010556static union node *
10557andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010558{
Eric Andersencb57d552001-06-28 07:25:16 +000010559 union node *n1, *n2, *n3;
10560 int t;
10561
Eric Andersencb57d552001-06-28 07:25:16 +000010562 n1 = pipeline();
10563 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010564 t = readtoken();
10565 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010566 t = NAND;
10567 } else if (t == TOR) {
10568 t = NOR;
10569 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010570 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010571 return n1;
10572 }
Eric Andersenc470f442003-07-28 09:56:35 +000010573 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010574 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010575 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010576 n3->type = t;
10577 n3->nbinary.ch1 = n1;
10578 n3->nbinary.ch2 = n2;
10579 n1 = n3;
10580 }
10581}
10582
Eric Andersenc470f442003-07-28 09:56:35 +000010583static union node *
10584pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010585{
Eric Andersencb57d552001-06-28 07:25:16 +000010586 union node *n1, *n2, *pipenode;
10587 struct nodelist *lp, *prev;
10588 int negate;
10589
10590 negate = 0;
10591 TRACE(("pipeline: entered\n"));
10592 if (readtoken() == TNOT) {
10593 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010594 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010595 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010596 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010597 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010598 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010599 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010600 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010601 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010602 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010603 pipenode->npipe.cmdlist = lp;
10604 lp->n = n1;
10605 do {
10606 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010607 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010608 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010609 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010610 prev->next = lp;
10611 } while (readtoken() == TPIPE);
10612 lp->next = NULL;
10613 n1 = pipenode;
10614 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010615 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010616 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010617 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010618 n2->type = NNOT;
10619 n2->nnot.com = n1;
10620 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010621 }
10622 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010623}
10624
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010625static union node *
10626makename(void)
10627{
10628 union node *n;
10629
Denis Vlasenko597906c2008-02-20 16:38:54 +000010630 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010631 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010632 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010633 n->narg.text = wordtext;
10634 n->narg.backquote = backquotelist;
10635 return n;
10636}
10637
10638static void
10639fixredir(union node *n, const char *text, int err)
10640{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010641 int fd;
10642
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010643 TRACE(("Fix redir %s %d\n", text, err));
10644 if (!err)
10645 n->ndup.vname = NULL;
10646
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010647 fd = bb_strtou(text, NULL, 10);
10648 if (!errno && fd >= 0)
10649 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010650 else if (LONE_DASH(text))
10651 n->ndup.dupfd = -1;
10652 else {
10653 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010654 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010655 n->ndup.vname = makename();
10656 }
10657}
10658
10659/*
10660 * Returns true if the text contains nothing to expand (no dollar signs
10661 * or backquotes).
10662 */
10663static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010664noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010665{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010666 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010667
Denys Vlasenkocd716832009-11-28 22:14:02 +010010668 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010669 if (c == CTLQUOTEMARK)
10670 continue;
10671 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010672 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010673 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010674 return 0;
10675 }
10676 return 1;
10677}
10678
10679static void
10680parsefname(void)
10681{
10682 union node *n = redirnode;
10683
10684 if (readtoken() != TWORD)
10685 raise_error_unexpected_syntax(-1);
10686 if (n->type == NHERE) {
10687 struct heredoc *here = heredoc;
10688 struct heredoc *p;
10689 int i;
10690
10691 if (quoteflag == 0)
10692 n->type = NXHERE;
10693 TRACE(("Here document %d\n", n->type));
10694 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010695 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010696 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010697 here->eofmark = wordtext;
10698 here->next = NULL;
10699 if (heredoclist == NULL)
10700 heredoclist = here;
10701 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010702 for (p = heredoclist; p->next; p = p->next)
10703 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010704 p->next = here;
10705 }
10706 } else if (n->type == NTOFD || n->type == NFROMFD) {
10707 fixredir(n, wordtext, 0);
10708 } else {
10709 n->nfile.fname = makename();
10710 }
10711}
Eric Andersencb57d552001-06-28 07:25:16 +000010712
Eric Andersenc470f442003-07-28 09:56:35 +000010713static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010714simplecmd(void)
10715{
10716 union node *args, **app;
10717 union node *n = NULL;
10718 union node *vars, **vpp;
10719 union node **rpp, *redir;
10720 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010721#if ENABLE_ASH_BASH_COMPAT
10722 smallint double_brackets_flag = 0;
10723#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010724
10725 args = NULL;
10726 app = &args;
10727 vars = NULL;
10728 vpp = &vars;
10729 redir = NULL;
10730 rpp = &redir;
10731
10732 savecheckkwd = CHKALIAS;
10733 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010734 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010735 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010736 t = readtoken();
10737 switch (t) {
10738#if ENABLE_ASH_BASH_COMPAT
10739 case TAND: /* "&&" */
10740 case TOR: /* "||" */
10741 if (!double_brackets_flag) {
10742 tokpushback = 1;
10743 goto out;
10744 }
10745 wordtext = (char *) (t == TAND ? "-a" : "-o");
10746#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010747 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010748 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010749 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010750 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010751 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010752#if ENABLE_ASH_BASH_COMPAT
10753 if (strcmp("[[", wordtext) == 0)
10754 double_brackets_flag = 1;
10755 else if (strcmp("]]", wordtext) == 0)
10756 double_brackets_flag = 0;
10757#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010758 n->narg.backquote = backquotelist;
10759 if (savecheckkwd && isassignment(wordtext)) {
10760 *vpp = n;
10761 vpp = &n->narg.next;
10762 } else {
10763 *app = n;
10764 app = &n->narg.next;
10765 savecheckkwd = 0;
10766 }
10767 break;
10768 case TREDIR:
10769 *rpp = n = redirnode;
10770 rpp = &n->nfile.next;
10771 parsefname(); /* read name of redirection file */
10772 break;
10773 case TLP:
10774 if (args && app == &args->narg.next
10775 && !vars && !redir
10776 ) {
10777 struct builtincmd *bcmd;
10778 const char *name;
10779
10780 /* We have a function */
10781 if (readtoken() != TRP)
10782 raise_error_unexpected_syntax(TRP);
10783 name = n->narg.text;
10784 if (!goodname(name)
10785 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10786 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010787 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010788 }
10789 n->type = NDEFUN;
10790 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10791 n->narg.next = parse_command();
10792 return n;
10793 }
10794 /* fall through */
10795 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010796 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010797 goto out;
10798 }
10799 }
10800 out:
10801 *app = NULL;
10802 *vpp = NULL;
10803 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010804 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010805 n->type = NCMD;
10806 n->ncmd.args = args;
10807 n->ncmd.assign = vars;
10808 n->ncmd.redirect = redir;
10809 return n;
10810}
10811
10812static union node *
10813parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010814{
Eric Andersencb57d552001-06-28 07:25:16 +000010815 union node *n1, *n2;
10816 union node *ap, **app;
10817 union node *cp, **cpp;
10818 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010819 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010820 int t;
10821
10822 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010823 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010824
Eric Andersencb57d552001-06-28 07:25:16 +000010825 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010826 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010827 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010828 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010829 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010830 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010831 n1->type = NIF;
10832 n1->nif.test = list(0);
10833 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010834 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010835 n1->nif.ifpart = list(0);
10836 n2 = n1;
10837 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010838 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010839 n2 = n2->nif.elsepart;
10840 n2->type = NIF;
10841 n2->nif.test = list(0);
10842 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010843 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010844 n2->nif.ifpart = list(0);
10845 }
10846 if (lasttoken == TELSE)
10847 n2->nif.elsepart = list(0);
10848 else {
10849 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010850 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010851 }
Eric Andersenc470f442003-07-28 09:56:35 +000010852 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010853 break;
10854 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010855 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010856 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010857 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010858 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010859 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010860 got = readtoken();
10861 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010862 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010863 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010864 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010865 }
10866 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010867 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010868 break;
10869 }
10870 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010871 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010872 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010873 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010874 n1->type = NFOR;
10875 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010876 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 if (readtoken() == TIN) {
10878 app = &ap;
10879 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010880 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010881 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010882 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010883 n2->narg.text = wordtext;
10884 n2->narg.backquote = backquotelist;
10885 *app = n2;
10886 app = &n2->narg.next;
10887 }
10888 *app = NULL;
10889 n1->nfor.args = ap;
10890 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010891 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010892 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010893 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010894 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010895 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010896 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010897 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010898 n1->nfor.args = n2;
10899 /*
10900 * Newline or semicolon here is optional (but note
10901 * that the original Bourne shell only allowed NL).
10902 */
10903 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010904 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010905 }
Eric Andersenc470f442003-07-28 09:56:35 +000010906 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010907 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010908 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010909 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010910 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010911 break;
10912 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010913 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010914 n1->type = NCASE;
10915 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010916 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010917 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010918 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010919 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010920 n2->narg.text = wordtext;
10921 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010922 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010923 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010924 } while (readtoken() == TNL);
10925 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010926 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010927 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010928 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010929 checkkwd = CHKNL | CHKKWD;
10930 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010931 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010932 if (lasttoken == TLP)
10933 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010934 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010935 cp->type = NCLIST;
10936 app = &cp->nclist.pattern;
10937 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010938 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010939 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010940 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010941 ap->narg.text = wordtext;
10942 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010943 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010944 break;
10945 app = &ap->narg.next;
10946 readtoken();
10947 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010948 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010949 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010950 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010951 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010952
Eric Andersenc470f442003-07-28 09:56:35 +000010953 cpp = &cp->nclist.next;
10954
10955 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010956 t = readtoken();
10957 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010958 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010959 raise_error_unexpected_syntax(TENDCASE);
10960 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010961 }
Eric Andersenc470f442003-07-28 09:56:35 +000010962 }
Eric Andersencb57d552001-06-28 07:25:16 +000010963 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010964 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010965 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010966 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010967 n1->type = NSUBSHELL;
10968 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010969 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010970 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010971 break;
10972 case TBEGIN:
10973 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010974 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010975 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010976 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010977 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010978 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010979 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010980 }
10981
Eric Andersenc470f442003-07-28 09:56:35 +000010982 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010983 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010984
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010985 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010986 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010987 checkkwd = CHKKWD | CHKALIAS;
10988 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010989 while (readtoken() == TREDIR) {
10990 *rpp = n2 = redirnode;
10991 rpp = &n2->nfile.next;
10992 parsefname();
10993 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010994 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010995 *rpp = NULL;
10996 if (redir) {
10997 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010998 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010999 n2->type = NREDIR;
11000 n2->nredir.n = n1;
11001 n1 = n2;
11002 }
11003 n1->nredir.redirect = redir;
11004 }
Eric Andersencb57d552001-06-28 07:25:16 +000011005 return n1;
11006}
11007
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011008#if ENABLE_ASH_BASH_COMPAT
11009static int decode_dollar_squote(void)
11010{
11011 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11012 int c, cnt;
11013 char *p;
11014 char buf[4];
11015
11016 c = pgetc();
11017 p = strchr(C_escapes, c);
11018 if (p) {
11019 buf[0] = c;
11020 p = buf;
11021 cnt = 3;
11022 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11023 do {
11024 c = pgetc();
11025 *++p = c;
11026 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11027 pungetc();
11028 } else if (c == 'x') { /* \xHH */
11029 do {
11030 c = pgetc();
11031 *++p = c;
11032 } while (isxdigit(c) && --cnt);
11033 pungetc();
11034 if (cnt == 3) { /* \x but next char is "bad" */
11035 c = 'x';
11036 goto unrecognized;
11037 }
11038 } else { /* simple seq like \\ or \t */
11039 p++;
11040 }
11041 *p = '\0';
11042 p = buf;
11043 c = bb_process_escape_sequence((void*)&p);
11044 } else { /* unrecognized "\z": print both chars unless ' or " */
11045 if (c != '\'' && c != '"') {
11046 unrecognized:
11047 c |= 0x100; /* "please encode \, then me" */
11048 }
11049 }
11050 return c;
11051}
11052#endif
11053
Eric Andersencb57d552001-06-28 07:25:16 +000011054/*
11055 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11056 * is not NULL, read a here document. In the latter case, eofmark is the
11057 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011058 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011059 * is the first character of the input token or document.
11060 *
11061 * Because C does not have internal subroutines, I have simulated them
11062 * using goto's to implement the subroutine linkage. The following macros
11063 * will run code that appears at the end of readtoken1.
11064 */
Eric Andersen2870d962001-07-02 17:27:21 +000011065#define CHECKEND() {goto checkend; checkend_return:;}
11066#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11067#define PARSESUB() {goto parsesub; parsesub_return:;}
11068#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11069#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11070#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011071static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011072readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011073{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011074 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011075 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011076 char *out;
11077 int len;
11078 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011079 struct nodelist *bqlist;
11080 smallint quotef;
11081 smallint dblquote;
11082 smallint oldstyle;
11083 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011084#if ENABLE_ASH_EXPAND_PRMT
11085 smallint pssyntax; /* we are expanding a prompt string */
11086#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011087 int varnest; /* levels of variables expansion */
11088 int arinest; /* levels of arithmetic expansion */
11089 int parenlevel; /* levels of parens in arithmetic */
11090 int dqvarnest; /* levels of variables expansion within double quotes */
11091
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011092 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011093
Eric Andersencb57d552001-06-28 07:25:16 +000011094#if __GNUC__
11095 /* Avoid longjmp clobbering */
11096 (void) &out;
11097 (void) &quotef;
11098 (void) &dblquote;
11099 (void) &varnest;
11100 (void) &arinest;
11101 (void) &parenlevel;
11102 (void) &dqvarnest;
11103 (void) &oldstyle;
11104 (void) &prevsyntax;
11105 (void) &syntax;
11106#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011107 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011108 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011109 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011110 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011111#if ENABLE_ASH_EXPAND_PRMT
11112 pssyntax = (syntax == PSSYNTAX);
11113 if (pssyntax)
11114 syntax = DQSYNTAX;
11115#endif
11116 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011117 varnest = 0;
11118 arinest = 0;
11119 parenlevel = 0;
11120 dqvarnest = 0;
11121
11122 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011123 loop:
11124 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011125 CHECKEND(); /* set c to PEOF if at end of here document */
11126 for (;;) { /* until end of line or end of word */
11127 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11128 switch (SIT(c, syntax)) {
11129 case CNL: /* '\n' */
11130 if (syntax == BASESYNTAX)
11131 goto endword; /* exit outer loop */
11132 USTPUTC(c, out);
11133 g_parsefile->linno++;
11134 setprompt_if(doprompt, 2);
11135 c = pgetc();
11136 goto loop; /* continue outer loop */
11137 case CWORD:
11138 USTPUTC(c, out);
11139 break;
11140 case CCTL:
11141 if (eofmark == NULL || dblquote)
11142 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011143#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011144 if (c == '\\' && bash_dollar_squote) {
11145 c = decode_dollar_squote();
11146 if (c & 0x100) {
11147 USTPUTC('\\', out);
11148 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011149 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011150 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011151#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011152 USTPUTC(c, out);
11153 break;
11154 case CBACK: /* backslash */
11155 c = pgetc_without_PEOA();
11156 if (c == PEOF) {
11157 USTPUTC(CTLESC, out);
11158 USTPUTC('\\', out);
11159 pungetc();
11160 } else if (c == '\n') {
11161 setprompt_if(doprompt, 2);
11162 } else {
11163#if ENABLE_ASH_EXPAND_PRMT
11164 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011165 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011166 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011167 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011168#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011169 /* Backslash is retained if we are in "str" and next char isn't special */
11170 if (dblquote
11171 && c != '\\'
11172 && c != '`'
11173 && c != '$'
11174 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011175 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011176 USTPUTC(CTLESC, out);
11177 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011178 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011179 if (SIT(c, SQSYNTAX) == CCTL)
11180 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011181 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011182 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011183 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011184 break;
11185 case CSQUOTE:
11186 syntax = SQSYNTAX;
11187 quotemark:
11188 if (eofmark == NULL) {
11189 USTPUTC(CTLQUOTEMARK, out);
11190 }
11191 break;
11192 case CDQUOTE:
11193 syntax = DQSYNTAX;
11194 dblquote = 1;
11195 goto quotemark;
11196 case CENDQUOTE:
11197 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11198 if (eofmark != NULL && arinest == 0
11199 && varnest == 0
11200 ) {
11201 USTPUTC(c, out);
11202 } else {
11203 if (dqvarnest == 0) {
11204 syntax = BASESYNTAX;
11205 dblquote = 0;
11206 }
11207 quotef = 1;
11208 goto quotemark;
11209 }
11210 break;
11211 case CVAR: /* '$' */
11212 PARSESUB(); /* parse substitution */
11213 break;
11214 case CENDVAR: /* '}' */
11215 if (varnest > 0) {
11216 varnest--;
11217 if (dqvarnest > 0) {
11218 dqvarnest--;
11219 }
11220 c = CTLENDVAR;
11221 }
11222 USTPUTC(c, out);
11223 break;
11224#if ENABLE_SH_MATH_SUPPORT
11225 case CLP: /* '(' in arithmetic */
11226 parenlevel++;
11227 USTPUTC(c, out);
11228 break;
11229 case CRP: /* ')' in arithmetic */
11230 if (parenlevel > 0) {
11231 parenlevel--;
11232 } else {
11233 if (pgetc() == ')') {
11234 if (--arinest == 0) {
11235 syntax = prevsyntax;
11236 dblquote = (syntax == DQSYNTAX);
11237 c = CTLENDARI;
11238 }
11239 } else {
11240 /*
11241 * unbalanced parens
11242 * (don't 2nd guess - no error)
11243 */
11244 pungetc();
11245 }
11246 }
11247 USTPUTC(c, out);
11248 break;
11249#endif
11250 case CBQUOTE: /* '`' */
11251 PARSEBACKQOLD();
11252 break;
11253 case CENDFILE:
11254 goto endword; /* exit outer loop */
11255 case CIGN:
11256 break;
11257 default:
11258 if (varnest == 0) {
11259#if ENABLE_ASH_BASH_COMPAT
11260 if (c == '&') {
11261 if (pgetc() == '>')
11262 c = 0x100 + '>'; /* flag &> */
11263 pungetc();
11264 }
11265#endif
11266 goto endword; /* exit outer loop */
11267 }
11268 IF_ASH_ALIAS(if (c != PEOA))
11269 USTPUTC(c, out);
11270 }
11271 c = pgetc_fast();
11272 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011273 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011274
Mike Frysinger98c52642009-04-02 10:02:37 +000011275#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011276 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011277 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011278#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011279 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011280 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011281 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011282 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011283 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011284 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011285 }
11286 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011287 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011288 out = stackblock();
11289 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011290 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011291 && quotef == 0
11292 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011293 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011294 PARSEREDIR(); /* passed as params: out, c */
11295 lasttoken = TREDIR;
11296 return lasttoken;
11297 }
11298 /* else: non-number X seen, interpret it
11299 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011300 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011301 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011302 }
11303 quoteflag = quotef;
11304 backquotelist = bqlist;
11305 grabstackblock(len);
11306 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011307 lasttoken = TWORD;
11308 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011309/* end of readtoken routine */
11310
Eric Andersencb57d552001-06-28 07:25:16 +000011311/*
11312 * Check to see whether we are at the end of the here document. When this
11313 * is called, c is set to the first character of the next input line. If
11314 * we are at the end of the here document, this routine sets the c to PEOF.
11315 */
Eric Andersenc470f442003-07-28 09:56:35 +000011316checkend: {
11317 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011318#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011319 if (c == PEOA)
11320 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011321#endif
11322 if (striptabs) {
11323 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011324 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011325 }
Eric Andersenc470f442003-07-28 09:56:35 +000011326 }
11327 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011328 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011329 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011330
Eric Andersenc470f442003-07-28 09:56:35 +000011331 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011332 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11333 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011334 if (*p == '\n' && *q == '\0') {
11335 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011336 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011337 needprompt = doprompt;
11338 } else {
11339 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011340 }
11341 }
11342 }
11343 }
Eric Andersenc470f442003-07-28 09:56:35 +000011344 goto checkend_return;
11345}
Eric Andersencb57d552001-06-28 07:25:16 +000011346
Eric Andersencb57d552001-06-28 07:25:16 +000011347/*
11348 * Parse a redirection operator. The variable "out" points to a string
11349 * specifying the fd to be redirected. The variable "c" contains the
11350 * first character of the redirection operator.
11351 */
Eric Andersenc470f442003-07-28 09:56:35 +000011352parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011353 /* out is already checked to be a valid number or "" */
11354 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011355 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011356
Denis Vlasenko597906c2008-02-20 16:38:54 +000011357 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011358 if (c == '>') {
11359 np->nfile.fd = 1;
11360 c = pgetc();
11361 if (c == '>')
11362 np->type = NAPPEND;
11363 else if (c == '|')
11364 np->type = NCLOBBER;
11365 else if (c == '&')
11366 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011367 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011368 else {
11369 np->type = NTO;
11370 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011371 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011372 }
11373#if ENABLE_ASH_BASH_COMPAT
11374 else if (c == 0x100 + '>') { /* this flags &> redirection */
11375 np->nfile.fd = 1;
11376 pgetc(); /* this is '>', no need to check */
11377 np->type = NTO2;
11378 }
11379#endif
11380 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011381 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011382 c = pgetc();
11383 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011384 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011385 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011386 np = stzalloc(sizeof(struct nhere));
11387 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011388 }
11389 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011390 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011391 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011392 c = pgetc();
11393 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011394 heredoc->striptabs = 1;
11395 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011396 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 pungetc();
11398 }
11399 break;
11400
11401 case '&':
11402 np->type = NFROMFD;
11403 break;
11404
11405 case '>':
11406 np->type = NFROMTO;
11407 break;
11408
11409 default:
11410 np->type = NFROM;
11411 pungetc();
11412 break;
11413 }
Eric Andersencb57d552001-06-28 07:25:16 +000011414 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011415 if (fd >= 0)
11416 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011417 redirnode = np;
11418 goto parseredir_return;
11419}
Eric Andersencb57d552001-06-28 07:25:16 +000011420
Eric Andersencb57d552001-06-28 07:25:16 +000011421/*
11422 * Parse a substitution. At this point, we have read the dollar sign
11423 * and nothing else.
11424 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011425
11426/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11427 * (assuming ascii char codes, as the original implementation did) */
11428#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011429 (((unsigned)(c) - 33 < 32) \
11430 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011431parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011432 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011433 int typeloc;
11434 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011435
Eric Andersenc470f442003-07-28 09:56:35 +000011436 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011437 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011438 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011439 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011440#if ENABLE_ASH_BASH_COMPAT
11441 if (c == '\'')
11442 bash_dollar_squote = 1;
11443 else
11444#endif
11445 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011446 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011447 } else if (c == '(') {
11448 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011449 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011450#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011451 PARSEARITH();
11452#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011453 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011454#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011455 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011456 pungetc();
11457 PARSEBACKQNEW();
11458 }
11459 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011460 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011461 USTPUTC(CTLVAR, out);
11462 typeloc = out - (char *)stackblock();
11463 USTPUTC(VSNORMAL, out);
11464 subtype = VSNORMAL;
11465 if (c == '{') {
11466 c = pgetc();
11467 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011468 c = pgetc();
11469 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011470 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011471 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011472 subtype = VSLENGTH; /* ${#VAR} */
11473 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011474 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011475 }
Eric Andersenc470f442003-07-28 09:56:35 +000011476 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011477 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011478 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011479 do {
11480 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011481 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011482 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011483 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011484 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011485 do {
11486 STPUTC(c, out);
11487 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011488 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011489 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011490 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011491 USTPUTC(c, out);
11492 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011493 } else {
11494 badsub:
11495 raise_error_syntax("bad substitution");
11496 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011497 if (c != '}' && subtype == VSLENGTH) {
11498 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011499 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011500 }
Eric Andersencb57d552001-06-28 07:25:16 +000011501
Eric Andersenc470f442003-07-28 09:56:35 +000011502 STPUTC('=', out);
11503 flags = 0;
11504 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011505 /* ${VAR...} but not $VAR or ${#VAR} */
11506 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011507 switch (c) {
11508 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011509 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011510#if ENABLE_ASH_BASH_COMPAT
11511 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011512//TODO: support more general format ${v:EXPR:EXPR},
11513// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011514 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011515 pungetc();
11516 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011517 }
11518#endif
11519 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011520 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011521 default: {
11522 static const char types[] ALIGN1 = "}-+?=";
11523 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011524 if (p == NULL)
11525 goto badsub;
11526 subtype = p - types + VSNORMAL;
11527 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011528 }
Eric Andersenc470f442003-07-28 09:56:35 +000011529 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011530 case '#': {
11531 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011532 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011533 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011534 if (c != cc)
11535 goto do_pungetc;
11536 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011537 break;
11538 }
11539#if ENABLE_ASH_BASH_COMPAT
11540 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011541 /* ${v/[/]pattern/repl} */
11542//TODO: encode pattern and repl separately.
11543// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011544 subtype = VSREPLACE;
11545 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011546 if (c != '/')
11547 goto do_pungetc;
11548 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011549 break;
11550#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011551 }
Eric Andersenc470f442003-07-28 09:56:35 +000011552 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011553 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011554 pungetc();
11555 }
11556 if (dblquote || arinest)
11557 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011558 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011559 if (subtype != VSNORMAL) {
11560 varnest++;
11561 if (dblquote || arinest) {
11562 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011563 }
11564 }
11565 }
Eric Andersenc470f442003-07-28 09:56:35 +000011566 goto parsesub_return;
11567}
Eric Andersencb57d552001-06-28 07:25:16 +000011568
Eric Andersencb57d552001-06-28 07:25:16 +000011569/*
11570 * Called to parse command substitutions. Newstyle is set if the command
11571 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11572 * list of commands (passed by reference), and savelen is the number of
11573 * characters on the top of the stack which must be preserved.
11574 */
Eric Andersenc470f442003-07-28 09:56:35 +000011575parsebackq: {
11576 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011577 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011578 union node *n;
11579 char *volatile str;
11580 struct jmploc jmploc;
11581 struct jmploc *volatile savehandler;
11582 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011583 smallint saveprompt = 0;
11584
Eric Andersencb57d552001-06-28 07:25:16 +000011585#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011586 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011587#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011588 savepbq = parsebackquote;
11589 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011590 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011591 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011592 exception_handler = savehandler;
11593 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011594 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011595 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011596 str = NULL;
11597 savelen = out - (char *)stackblock();
11598 if (savelen > 0) {
11599 str = ckmalloc(savelen);
11600 memcpy(str, stackblock(), savelen);
11601 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011602 savehandler = exception_handler;
11603 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011604 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011605 if (oldstyle) {
11606 /* We must read until the closing backquote, giving special
11607 treatment to some slashes, and then push the string and
11608 reread it as input, interpreting it normally. */
11609 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011610 size_t psavelen;
11611 char *pstr;
11612
Eric Andersenc470f442003-07-28 09:56:35 +000011613 STARTSTACKSTR(pout);
11614 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011615 int pc;
11616
11617 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011618 pc = pgetc();
11619 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011620 case '`':
11621 goto done;
11622
11623 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011624 pc = pgetc();
11625 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011626 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011627 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011628 /*
11629 * If eating a newline, avoid putting
11630 * the newline into the new character
11631 * stream (via the STPUTC after the
11632 * switch).
11633 */
11634 continue;
11635 }
11636 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011637 && (!dblquote || pc != '"')
11638 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011639 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011640 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011641 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011642 break;
11643 }
11644 /* fall through */
11645
11646 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011647 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011648 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011649 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011650
11651 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011652 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011653 needprompt = doprompt;
11654 break;
11655
11656 default:
11657 break;
11658 }
11659 STPUTC(pc, pout);
11660 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011661 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011662 STPUTC('\0', pout);
11663 psavelen = pout - (char *)stackblock();
11664 if (psavelen > 0) {
11665 pstr = grabstackstr(pout);
11666 setinputstring(pstr);
11667 }
11668 }
11669 nlpp = &bqlist;
11670 while (*nlpp)
11671 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011672 *nlpp = stzalloc(sizeof(**nlpp));
11673 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011674 parsebackquote = oldstyle;
11675
11676 if (oldstyle) {
11677 saveprompt = doprompt;
11678 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011679 }
11680
Eric Andersenc470f442003-07-28 09:56:35 +000011681 n = list(2);
11682
11683 if (oldstyle)
11684 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011685 else if (readtoken() != TRP)
11686 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011687
11688 (*nlpp)->n = n;
11689 if (oldstyle) {
11690 /*
11691 * Start reading from old file again, ignoring any pushed back
11692 * tokens left from the backquote parsing
11693 */
11694 popfile();
11695 tokpushback = 0;
11696 }
11697 while (stackblocksize() <= savelen)
11698 growstackblock();
11699 STARTSTACKSTR(out);
11700 if (str) {
11701 memcpy(out, str, savelen);
11702 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011703 INT_OFF;
11704 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011705 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011706 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011707 }
11708 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011709 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011710 if (arinest || dblquote)
11711 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11712 else
11713 USTPUTC(CTLBACKQ, out);
11714 if (oldstyle)
11715 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011716 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011717}
11718
Mike Frysinger98c52642009-04-02 10:02:37 +000011719#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011720/*
11721 * Parse an arithmetic expansion (indicate start of one and set state)
11722 */
Eric Andersenc470f442003-07-28 09:56:35 +000011723parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011724 if (++arinest == 1) {
11725 prevsyntax = syntax;
11726 syntax = ARISYNTAX;
11727 USTPUTC(CTLARI, out);
11728 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011729 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011730 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011731 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011732 } else {
11733 /*
11734 * we collapse embedded arithmetic expansion to
11735 * parenthesis, which should be equivalent
11736 */
11737 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011738 }
Eric Andersenc470f442003-07-28 09:56:35 +000011739 goto parsearith_return;
11740}
11741#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011742
Eric Andersenc470f442003-07-28 09:56:35 +000011743} /* end of readtoken */
11744
Eric Andersencb57d552001-06-28 07:25:16 +000011745/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011746 * Read the next input token.
11747 * If the token is a word, we set backquotelist to the list of cmds in
11748 * backquotes. We set quoteflag to true if any part of the word was
11749 * quoted.
11750 * If the token is TREDIR, then we set redirnode to a structure containing
11751 * the redirection.
11752 * In all cases, the variable startlinno is set to the number of the line
11753 * on which the token starts.
11754 *
11755 * [Change comment: here documents and internal procedures]
11756 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11757 * word parsing code into a separate routine. In this case, readtoken
11758 * doesn't need to have any internal procedures, but parseword does.
11759 * We could also make parseoperator in essence the main routine, and
11760 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011761 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011762#define NEW_xxreadtoken
11763#ifdef NEW_xxreadtoken
11764/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011765static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011766 '\n', '(', ')', /* singles */
11767 '&', '|', ';', /* doubles */
11768 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011769};
Eric Andersencb57d552001-06-28 07:25:16 +000011770
Denis Vlasenko834dee72008-10-07 09:18:30 +000011771#define xxreadtoken_singles 3
11772#define xxreadtoken_doubles 3
11773
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011774static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011775 TNL, TLP, TRP, /* only single occurrence allowed */
11776 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11777 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011778 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011779};
11780
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011781static int
11782xxreadtoken(void)
11783{
11784 int c;
11785
11786 if (tokpushback) {
11787 tokpushback = 0;
11788 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011789 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011790 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011791 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011792 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011793 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011794 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011795 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011796
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011797 if (c == '#') {
11798 while ((c = pgetc()) != '\n' && c != PEOF)
11799 continue;
11800 pungetc();
11801 } else if (c == '\\') {
11802 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011803 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011804 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011806 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011807 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011808 } else {
11809 const char *p;
11810
11811 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11812 if (c != PEOF) {
11813 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011814 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011815 needprompt = doprompt;
11816 }
11817
11818 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011819 if (p == NULL)
11820 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011821
Denis Vlasenko834dee72008-10-07 09:18:30 +000011822 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11823 int cc = pgetc();
11824 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011825 p += xxreadtoken_doubles + 1;
11826 } else {
11827 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011828#if ENABLE_ASH_BASH_COMPAT
11829 if (c == '&' && cc == '>') /* &> */
11830 break; /* return readtoken1(...) */
11831#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011832 }
11833 }
11834 }
11835 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11836 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011837 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011838 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011839
11840 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011841}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011842#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011843#define RETURN(token) return lasttoken = token
11844static int
11845xxreadtoken(void)
11846{
11847 int c;
11848
11849 if (tokpushback) {
11850 tokpushback = 0;
11851 return lasttoken;
11852 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011853 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011854 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011855 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011856 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011857 switch (c) {
11858 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011859 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011860 continue;
11861 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011862 while ((c = pgetc()) != '\n' && c != PEOF)
11863 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011864 pungetc();
11865 continue;
11866 case '\\':
11867 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011868 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011869 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011870 continue;
11871 }
11872 pungetc();
11873 goto breakloop;
11874 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011875 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011876 needprompt = doprompt;
11877 RETURN(TNL);
11878 case PEOF:
11879 RETURN(TEOF);
11880 case '&':
11881 if (pgetc() == '&')
11882 RETURN(TAND);
11883 pungetc();
11884 RETURN(TBACKGND);
11885 case '|':
11886 if (pgetc() == '|')
11887 RETURN(TOR);
11888 pungetc();
11889 RETURN(TPIPE);
11890 case ';':
11891 if (pgetc() == ';')
11892 RETURN(TENDCASE);
11893 pungetc();
11894 RETURN(TSEMI);
11895 case '(':
11896 RETURN(TLP);
11897 case ')':
11898 RETURN(TRP);
11899 default:
11900 goto breakloop;
11901 }
11902 }
11903 breakloop:
11904 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11905#undef RETURN
11906}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011907#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011908
11909static int
11910readtoken(void)
11911{
11912 int t;
11913#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011914 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011915#endif
11916
11917#if ENABLE_ASH_ALIAS
11918 top:
11919#endif
11920
11921 t = xxreadtoken();
11922
11923 /*
11924 * eat newlines
11925 */
11926 if (checkkwd & CHKNL) {
11927 while (t == TNL) {
11928 parseheredoc();
11929 t = xxreadtoken();
11930 }
11931 }
11932
11933 if (t != TWORD || quoteflag) {
11934 goto out;
11935 }
11936
11937 /*
11938 * check for keywords
11939 */
11940 if (checkkwd & CHKKWD) {
11941 const char *const *pp;
11942
11943 pp = findkwd(wordtext);
11944 if (pp) {
11945 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011946 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011947 goto out;
11948 }
11949 }
11950
11951 if (checkkwd & CHKALIAS) {
11952#if ENABLE_ASH_ALIAS
11953 struct alias *ap;
11954 ap = lookupalias(wordtext, 1);
11955 if (ap != NULL) {
11956 if (*ap->val) {
11957 pushstring(ap->val, ap);
11958 }
11959 goto top;
11960 }
11961#endif
11962 }
11963 out:
11964 checkkwd = 0;
11965#if DEBUG
11966 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011967 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011968 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011969 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011970#endif
11971 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011972}
11973
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011974static char
11975peektoken(void)
11976{
11977 int t;
11978
11979 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011980 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011981 return tokname_array[t][0];
11982}
Eric Andersencb57d552001-06-28 07:25:16 +000011983
11984/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011985 * Read and parse a command. Returns NODE_EOF on end of file.
11986 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011987 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011988static union node *
11989parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011990{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011991 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011992
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011993 tokpushback = 0;
11994 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011995 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011996 needprompt = 0;
11997 t = readtoken();
11998 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011999 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012000 if (t == TNL)
12001 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012002 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012003 return list(1);
12004}
12005
12006/*
12007 * Input any here documents.
12008 */
12009static void
12010parseheredoc(void)
12011{
12012 struct heredoc *here;
12013 union node *n;
12014
12015 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012016 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012017
12018 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012019 setprompt_if(needprompt, 2);
12020 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012021 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012022 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012023 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012024 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012025 n->narg.text = wordtext;
12026 n->narg.backquote = backquotelist;
12027 here->here->nhere.doc = n;
12028 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012029 }
Eric Andersencb57d552001-06-28 07:25:16 +000012030}
12031
12032
12033/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012034 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012035 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012036#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012037static const char *
12038expandstr(const char *ps)
12039{
12040 union node n;
12041
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012042 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12043 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012044 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012045 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012046 popfile();
12047
12048 n.narg.type = NARG;
12049 n.narg.next = NULL;
12050 n.narg.text = wordtext;
12051 n.narg.backquote = backquotelist;
12052
12053 expandarg(&n, NULL, 0);
12054 return stackblock();
12055}
12056#endif
12057
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012058/*
12059 * Execute a command or commands contained in a string.
12060 */
12061static int
12062evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012063{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012064 union node *n;
12065 struct stackmark smark;
12066 int skip;
12067
12068 setinputstring(s);
12069 setstackmark(&smark);
12070
12071 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012072 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012073 evaltree(n, 0);
12074 popstackmark(&smark);
12075 skip = evalskip;
12076 if (skip)
12077 break;
12078 }
12079 popfile();
12080
12081 skip &= mask;
12082 evalskip = skip;
12083 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012084}
12085
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012086/*
12087 * The eval command.
12088 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012089static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012090evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012091{
12092 char *p;
12093 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012094
Denis Vlasenko68404f12008-03-17 09:00:54 +000012095 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012096 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012097 argv += 2;
12098 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012099 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012100 for (;;) {
12101 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012102 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012103 if (p == NULL)
12104 break;
12105 STPUTC(' ', concat);
12106 }
12107 STPUTC('\0', concat);
12108 p = grabstackstr(concat);
12109 }
12110 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012111 }
12112 return exitstatus;
12113}
12114
12115/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012116 * Read and execute commands.
12117 * "Top" is nonzero for the top level command loop;
12118 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012119 */
12120static int
12121cmdloop(int top)
12122{
12123 union node *n;
12124 struct stackmark smark;
12125 int inter;
12126 int numeof = 0;
12127
12128 TRACE(("cmdloop(%d) called\n", top));
12129 for (;;) {
12130 int skip;
12131
12132 setstackmark(&smark);
12133#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012134 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012135 showjobs(stderr, SHOW_CHANGED);
12136#endif
12137 inter = 0;
12138 if (iflag && top) {
12139 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012140 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012141 }
12142 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012143#if DEBUG
12144 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012145 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012146#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012147 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012148 if (!top || numeof >= 50)
12149 break;
12150 if (!stoppedjobs()) {
12151 if (!Iflag)
12152 break;
12153 out2str("\nUse \"exit\" to leave shell.\n");
12154 }
12155 numeof++;
12156 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012157 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12158 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012159 numeof = 0;
12160 evaltree(n, 0);
12161 }
12162 popstackmark(&smark);
12163 skip = evalskip;
12164
12165 if (skip) {
12166 evalskip = 0;
12167 return skip & SKIPEVAL;
12168 }
12169 }
12170 return 0;
12171}
12172
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012173/*
12174 * Take commands from a file. To be compatible we should do a path
12175 * search for the file, which is necessary to find sub-commands.
12176 */
12177static char *
12178find_dot_file(char *name)
12179{
12180 char *fullname;
12181 const char *path = pathval();
12182 struct stat statb;
12183
12184 /* don't try this for absolute or relative paths */
12185 if (strchr(name, '/'))
12186 return name;
12187
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012188 /* IIRC standards do not say whether . is to be searched.
12189 * And it is even smaller this way, making it unconditional for now:
12190 */
12191 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12192 fullname = name;
12193 goto try_cur_dir;
12194 }
12195
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012196 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012197 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012198 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12199 /*
12200 * Don't bother freeing here, since it will
12201 * be freed by the caller.
12202 */
12203 return fullname;
12204 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012205 if (fullname != name)
12206 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012207 }
12208
12209 /* not found in the PATH */
12210 ash_msg_and_raise_error("%s: not found", name);
12211 /* NOTREACHED */
12212}
12213
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012214static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012215dotcmd(int argc, char **argv)
12216{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012217 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012218 struct strlist *sp;
12219 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012220
12221 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012222 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012223
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012224 if (!argv[1]) {
12225 /* bash says: "bash: .: filename argument required" */
12226 return 2; /* bash compat */
12227 }
12228
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012229 /* "false; . empty_file; echo $?" should print 0, not 1: */
12230 exitstatus = 0;
12231
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012232 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012233
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012234 argv += 2;
12235 argc -= 2;
12236 if (argc) { /* argc > 0, argv[0] != NULL */
12237 saveparam = shellparam;
12238 shellparam.malloced = 0;
12239 shellparam.nparam = argc;
12240 shellparam.p = argv;
12241 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012242
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012243 setinputfile(fullname, INPUT_PUSH_FILE);
12244 commandname = fullname;
12245 cmdloop(0);
12246 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012247
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012248 if (argc) {
12249 freeparam(&shellparam);
12250 shellparam = saveparam;
12251 };
12252
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012253 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012254}
12255
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012256static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012257exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012258{
12259 if (stoppedjobs())
12260 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012261 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012262 exitstatus = number(argv[1]);
12263 raise_exception(EXEXIT);
12264 /* NOTREACHED */
12265}
12266
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012267/*
12268 * Read a file containing shell functions.
12269 */
12270static void
12271readcmdfile(char *name)
12272{
12273 setinputfile(name, INPUT_PUSH_FILE);
12274 cmdloop(0);
12275 popfile();
12276}
12277
12278
Denis Vlasenkocc571512007-02-23 21:10:35 +000012279/* ============ find_command inplementation */
12280
12281/*
12282 * Resolve a command name. If you change this routine, you may have to
12283 * change the shellexec routine as well.
12284 */
12285static void
12286find_command(char *name, struct cmdentry *entry, int act, const char *path)
12287{
12288 struct tblentry *cmdp;
12289 int idx;
12290 int prev;
12291 char *fullname;
12292 struct stat statb;
12293 int e;
12294 int updatetbl;
12295 struct builtincmd *bcmd;
12296
12297 /* If name contains a slash, don't use PATH or hash table */
12298 if (strchr(name, '/') != NULL) {
12299 entry->u.index = -1;
12300 if (act & DO_ABS) {
12301 while (stat(name, &statb) < 0) {
12302#ifdef SYSV
12303 if (errno == EINTR)
12304 continue;
12305#endif
12306 entry->cmdtype = CMDUNKNOWN;
12307 return;
12308 }
12309 }
12310 entry->cmdtype = CMDNORMAL;
12311 return;
12312 }
12313
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012314/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012315
12316 updatetbl = (path == pathval());
12317 if (!updatetbl) {
12318 act |= DO_ALTPATH;
12319 if (strstr(path, "%builtin") != NULL)
12320 act |= DO_ALTBLTIN;
12321 }
12322
12323 /* If name is in the table, check answer will be ok */
12324 cmdp = cmdlookup(name, 0);
12325 if (cmdp != NULL) {
12326 int bit;
12327
12328 switch (cmdp->cmdtype) {
12329 default:
12330#if DEBUG
12331 abort();
12332#endif
12333 case CMDNORMAL:
12334 bit = DO_ALTPATH;
12335 break;
12336 case CMDFUNCTION:
12337 bit = DO_NOFUNC;
12338 break;
12339 case CMDBUILTIN:
12340 bit = DO_ALTBLTIN;
12341 break;
12342 }
12343 if (act & bit) {
12344 updatetbl = 0;
12345 cmdp = NULL;
12346 } else if (cmdp->rehash == 0)
12347 /* if not invalidated by cd, we're done */
12348 goto success;
12349 }
12350
12351 /* If %builtin not in path, check for builtin next */
12352 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012353 if (bcmd) {
12354 if (IS_BUILTIN_REGULAR(bcmd))
12355 goto builtin_success;
12356 if (act & DO_ALTPATH) {
12357 if (!(act & DO_ALTBLTIN))
12358 goto builtin_success;
12359 } else if (builtinloc <= 0) {
12360 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012361 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012362 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012363
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012364#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012365 {
12366 int applet_no = find_applet_by_name(name);
12367 if (applet_no >= 0) {
12368 entry->cmdtype = CMDNORMAL;
12369 entry->u.index = -2 - applet_no;
12370 return;
12371 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012372 }
12373#endif
12374
Denis Vlasenkocc571512007-02-23 21:10:35 +000012375 /* We have to search path. */
12376 prev = -1; /* where to start */
12377 if (cmdp && cmdp->rehash) { /* doing a rehash */
12378 if (cmdp->cmdtype == CMDBUILTIN)
12379 prev = builtinloc;
12380 else
12381 prev = cmdp->param.index;
12382 }
12383
12384 e = ENOENT;
12385 idx = -1;
12386 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012387 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012388 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012389 /* NB: code below will still use fullname
12390 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012391 idx++;
12392 if (pathopt) {
12393 if (prefix(pathopt, "builtin")) {
12394 if (bcmd)
12395 goto builtin_success;
12396 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012397 }
12398 if ((act & DO_NOFUNC)
12399 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012400 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012401 continue;
12402 }
12403 }
12404 /* if rehash, don't redo absolute path names */
12405 if (fullname[0] == '/' && idx <= prev) {
12406 if (idx < prev)
12407 continue;
12408 TRACE(("searchexec \"%s\": no change\n", name));
12409 goto success;
12410 }
12411 while (stat(fullname, &statb) < 0) {
12412#ifdef SYSV
12413 if (errno == EINTR)
12414 continue;
12415#endif
12416 if (errno != ENOENT && errno != ENOTDIR)
12417 e = errno;
12418 goto loop;
12419 }
12420 e = EACCES; /* if we fail, this will be the error */
12421 if (!S_ISREG(statb.st_mode))
12422 continue;
12423 if (pathopt) { /* this is a %func directory */
12424 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012425 /* NB: stalloc will return space pointed by fullname
12426 * (because we don't have any intervening allocations
12427 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012428 readcmdfile(fullname);
12429 cmdp = cmdlookup(name, 0);
12430 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12431 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12432 stunalloc(fullname);
12433 goto success;
12434 }
12435 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12436 if (!updatetbl) {
12437 entry->cmdtype = CMDNORMAL;
12438 entry->u.index = idx;
12439 return;
12440 }
12441 INT_OFF;
12442 cmdp = cmdlookup(name, 1);
12443 cmdp->cmdtype = CMDNORMAL;
12444 cmdp->param.index = idx;
12445 INT_ON;
12446 goto success;
12447 }
12448
12449 /* We failed. If there was an entry for this command, delete it */
12450 if (cmdp && updatetbl)
12451 delete_cmd_entry();
12452 if (act & DO_ERR)
12453 ash_msg("%s: %s", name, errmsg(e, "not found"));
12454 entry->cmdtype = CMDUNKNOWN;
12455 return;
12456
12457 builtin_success:
12458 if (!updatetbl) {
12459 entry->cmdtype = CMDBUILTIN;
12460 entry->u.cmd = bcmd;
12461 return;
12462 }
12463 INT_OFF;
12464 cmdp = cmdlookup(name, 1);
12465 cmdp->cmdtype = CMDBUILTIN;
12466 cmdp->param.cmd = bcmd;
12467 INT_ON;
12468 success:
12469 cmdp->rehash = 0;
12470 entry->cmdtype = cmdp->cmdtype;
12471 entry->u = cmdp->param;
12472}
12473
12474
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012475/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012476
Eric Andersencb57d552001-06-28 07:25:16 +000012477/*
Eric Andersencb57d552001-06-28 07:25:16 +000012478 * The trap builtin.
12479 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012480static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012481trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012482{
12483 char *action;
12484 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012485 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012486
Eric Andersenc470f442003-07-28 09:56:35 +000012487 nextopt(nullstr);
12488 ap = argptr;
12489 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012490 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012491 char *tr = trap_ptr[signo];
12492 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012493 /* note: bash adds "SIG", but only if invoked
12494 * as "bash". If called as "sh", or if set -o posix,
12495 * then it prints short signal names.
12496 * We are printing short names: */
12497 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012498 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012499 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012500 /* trap_ptr != trap only if we are in special-cased `trap` code.
12501 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012502 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012503 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012504 }
12505 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012506 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012507 if (trap_ptr != trap) {
12508 free(trap_ptr);
12509 trap_ptr = trap;
12510 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012511 */
Eric Andersencb57d552001-06-28 07:25:16 +000012512 return 0;
12513 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012514
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012515 action = NULL;
12516 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012517 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012518 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012519 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012520 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012521 if (signo < 0) {
12522 /* Mimic bash message exactly */
12523 ash_msg("%s: invalid signal specification", *ap);
12524 exitcode = 1;
12525 goto next;
12526 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012527 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012528 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012529 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012530 action = NULL;
12531 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012532 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012533 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012534 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012535 if (action)
12536 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012537 trap[signo] = action;
12538 if (signo != 0)
12539 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012540 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012541 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012542 ap++;
12543 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012544 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012545}
12546
Eric Andersenc470f442003-07-28 09:56:35 +000012547
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012548/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012549
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012550#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012551/*
12552 * Lists available builtins
12553 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012554static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012555helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012556{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012557 unsigned col;
12558 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012559
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012560 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012561 "Built-in commands:\n"
12562 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012563 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012564 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012565 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012566 if (col > 60) {
12567 out1fmt("\n");
12568 col = 0;
12569 }
12570 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012571#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012572 {
12573 const char *a = applet_names;
12574 while (*a) {
12575 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12576 if (col > 60) {
12577 out1fmt("\n");
12578 col = 0;
12579 }
12580 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012581 }
12582 }
12583#endif
12584 out1fmt("\n\n");
12585 return EXIT_SUCCESS;
12586}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012587#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012588
Eric Andersencb57d552001-06-28 07:25:16 +000012589/*
Eric Andersencb57d552001-06-28 07:25:16 +000012590 * The export and readonly commands.
12591 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012592static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012593exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012594{
12595 struct var *vp;
12596 char *name;
12597 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012598 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012599 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012600
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012601 if (nextopt("p") != 'p') {
12602 aptr = argptr;
12603 name = *aptr;
12604 if (name) {
12605 do {
12606 p = strchr(name, '=');
12607 if (p != NULL) {
12608 p++;
12609 } else {
12610 vp = *findvar(hashvar(name), name);
12611 if (vp) {
12612 vp->flags |= flag;
12613 continue;
12614 }
Eric Andersencb57d552001-06-28 07:25:16 +000012615 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012616 setvar(name, p, flag);
12617 } while ((name = *++aptr) != NULL);
12618 return 0;
12619 }
Eric Andersencb57d552001-06-28 07:25:16 +000012620 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012621 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012622 return 0;
12623}
12624
Eric Andersencb57d552001-06-28 07:25:16 +000012625/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012626 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012627 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012628static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012629unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012630{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012631 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012632
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012633 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012634 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012635 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012636}
12637
Eric Andersencb57d552001-06-28 07:25:16 +000012638/*
Eric Andersencb57d552001-06-28 07:25:16 +000012639 * The unset builtin command. We unset the function before we unset the
12640 * variable to allow a function to be unset when there is a readonly variable
12641 * with the same name.
12642 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012643static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012644unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012645{
12646 char **ap;
12647 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012648 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012649 int ret = 0;
12650
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012651 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012652 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012653 }
Eric Andersencb57d552001-06-28 07:25:16 +000012654
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012655 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012656 if (flag != 'f') {
12657 i = unsetvar(*ap);
12658 ret |= i;
12659 if (!(i & 2))
12660 continue;
12661 }
12662 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012663 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012664 }
Eric Andersenc470f442003-07-28 09:56:35 +000012665 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012666}
12667
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012668static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012669 ' ', offsetof(struct tms, tms_utime),
12670 '\n', offsetof(struct tms, tms_stime),
12671 ' ', offsetof(struct tms, tms_cutime),
12672 '\n', offsetof(struct tms, tms_cstime),
12673 0
12674};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012675static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012676timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012677{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012678 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012679 const unsigned char *p;
12680 struct tms buf;
12681
12682 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012683 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012684
12685 p = timescmd_str;
12686 do {
12687 t = *(clock_t *)(((char *) &buf) + p[1]);
12688 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012689 t = t % clk_tck;
12690 out1fmt("%lum%lu.%03lus%c",
12691 s / 60, s % 60,
12692 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012693 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012694 p += 2;
12695 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012696
Eric Andersencb57d552001-06-28 07:25:16 +000012697 return 0;
12698}
12699
Mike Frysinger98c52642009-04-02 10:02:37 +000012700#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012701/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012702 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012703 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012704 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012705 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012706 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012707static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012708letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012709{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012710 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012711
Denis Vlasenko68404f12008-03-17 09:00:54 +000012712 argv++;
12713 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012714 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012715 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012716 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012717 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012718
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012719 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012720}
Eric Andersenc470f442003-07-28 09:56:35 +000012721#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012722
Eric Andersenc470f442003-07-28 09:56:35 +000012723/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012724 * The read builtin. Options:
12725 * -r Do not interpret '\' specially
12726 * -s Turn off echo (tty only)
12727 * -n NCHARS Read NCHARS max
12728 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12729 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12730 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012731 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012732 * TODO: bash also has:
12733 * -a ARRAY Read into array[0],[1],etc
12734 * -d DELIM End on DELIM char, not newline
12735 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012736 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012737static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012738readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012739{
Denys Vlasenko73067272010-01-12 22:11:24 +010012740 char *opt_n = NULL;
12741 char *opt_p = NULL;
12742 char *opt_t = NULL;
12743 char *opt_u = NULL;
12744 int read_flags = 0;
12745 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012746 int i;
12747
Denys Vlasenko73067272010-01-12 22:11:24 +010012748 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012749 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012750 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012751 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012752 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012753 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012754 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012755 break;
12756 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012757 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012758 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012759 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012760 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012761 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012762 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012763 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012764 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012765 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012766 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012767 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012768 default:
12769 break;
12770 }
Eric Andersenc470f442003-07-28 09:56:35 +000012771 }
Paul Fox02eb9342005-09-07 16:56:02 +000012772
Denys Vlasenko03dad222010-01-12 23:29:57 +010012773 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012774 argptr,
12775 bltinlookup("IFS"), /* can be NULL */
12776 read_flags,
12777 opt_n,
12778 opt_p,
12779 opt_t,
12780 opt_u
12781 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012782
Denys Vlasenko73067272010-01-12 22:11:24 +010012783 if ((uintptr_t)r > 1)
12784 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012785
Denys Vlasenko73067272010-01-12 22:11:24 +010012786 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012787}
12788
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012789static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012790umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012791{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012792 static const char permuser[3] ALIGN1 = "ugo";
12793 static const char permmode[3] ALIGN1 = "rwx";
12794 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012795 S_IRUSR, S_IWUSR, S_IXUSR,
12796 S_IRGRP, S_IWGRP, S_IXGRP,
12797 S_IROTH, S_IWOTH, S_IXOTH
12798 };
12799
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012800 /* TODO: use bb_parse_mode() instead */
12801
Eric Andersenc470f442003-07-28 09:56:35 +000012802 char *ap;
12803 mode_t mask;
12804 int i;
12805 int symbolic_mode = 0;
12806
12807 while (nextopt("S") != '\0') {
12808 symbolic_mode = 1;
12809 }
12810
Denis Vlasenkob012b102007-02-19 22:43:01 +000012811 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012812 mask = umask(0);
12813 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012814 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012815
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012816 ap = *argptr;
12817 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012818 if (symbolic_mode) {
12819 char buf[18];
12820 char *p = buf;
12821
12822 for (i = 0; i < 3; i++) {
12823 int j;
12824
12825 *p++ = permuser[i];
12826 *p++ = '=';
12827 for (j = 0; j < 3; j++) {
12828 if ((mask & permmask[3 * i + j]) == 0) {
12829 *p++ = permmode[j];
12830 }
12831 }
12832 *p++ = ',';
12833 }
12834 *--p = 0;
12835 puts(buf);
12836 } else {
12837 out1fmt("%.4o\n", mask);
12838 }
12839 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012840 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012841 mask = 0;
12842 do {
12843 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012844 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012845 mask = (mask << 3) + (*ap - '0');
12846 } while (*++ap != '\0');
12847 umask(mask);
12848 } else {
12849 mask = ~mask & 0777;
12850 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012851 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012852 }
12853 umask(~mask & 0777);
12854 }
12855 }
12856 return 0;
12857}
12858
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012859static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012860ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012861{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012862 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012863}
12864
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012865/* ============ main() and helpers */
12866
12867/*
12868 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012869 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012870static void
12871exitshell(void)
12872{
12873 struct jmploc loc;
12874 char *p;
12875 int status;
12876
12877 status = exitstatus;
12878 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12879 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012880 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012881/* dash bug: it just does _exit(exitstatus) here
12882 * but we have to do setjobctl(0) first!
12883 * (bug is still not fixed in dash-0.5.3 - if you run dash
12884 * under Midnight Commander, on exit from dash MC is backgrounded) */
12885 status = exitstatus;
12886 goto out;
12887 }
12888 exception_handler = &loc;
12889 p = trap[0];
12890 if (p) {
12891 trap[0] = NULL;
12892 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012893 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012894 }
12895 flush_stdout_stderr();
12896 out:
12897 setjobctl(0);
12898 _exit(status);
12899 /* NOTREACHED */
12900}
12901
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012902static void
12903init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012904{
12905 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012906 /* we will never free this */
12907 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012908
12909 /* from trap.c: */
12910 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012911 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12912 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12913 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012914 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012915
12916 /* from var.c: */
12917 {
12918 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012919 const char *p;
12920 struct stat st1, st2;
12921
12922 initvar();
12923 for (envp = environ; envp && *envp; envp++) {
12924 if (strchr(*envp, '=')) {
12925 setvareq(*envp, VEXPORT|VTEXTFIXED);
12926 }
12927 }
12928
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012929 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012930
12931 p = lookupvar("PWD");
12932 if (p)
12933 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12934 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12935 p = '\0';
12936 setpwd(p, 0);
12937 }
12938}
12939
12940/*
12941 * Process the shell command line arguments.
12942 */
12943static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012944procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012945{
12946 int i;
12947 const char *xminusc;
12948 char **xargv;
12949
12950 xargv = argv;
12951 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012952 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012953 xargv++;
12954 for (i = 0; i < NOPTS; i++)
12955 optlist[i] = 2;
12956 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012957 if (options(1)) {
12958 /* it already printed err message */
12959 raise_exception(EXERROR);
12960 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012961 xargv = argptr;
12962 xminusc = minusc;
12963 if (*xargv == NULL) {
12964 if (xminusc)
12965 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12966 sflag = 1;
12967 }
12968 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12969 iflag = 1;
12970 if (mflag == 2)
12971 mflag = iflag;
12972 for (i = 0; i < NOPTS; i++)
12973 if (optlist[i] == 2)
12974 optlist[i] = 0;
12975#if DEBUG == 2
12976 debug = 1;
12977#endif
12978 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12979 if (xminusc) {
12980 minusc = *xargv++;
12981 if (*xargv)
12982 goto setarg0;
12983 } else if (!sflag) {
12984 setinputfile(*xargv, 0);
12985 setarg0:
12986 arg0 = *xargv++;
12987 commandname = arg0;
12988 }
12989
12990 shellparam.p = xargv;
12991#if ENABLE_ASH_GETOPTS
12992 shellparam.optind = 1;
12993 shellparam.optoff = -1;
12994#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012995 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012996 while (*xargv) {
12997 shellparam.nparam++;
12998 xargv++;
12999 }
13000 optschanged();
13001}
13002
13003/*
13004 * Read /etc/profile or .profile.
13005 */
13006static void
13007read_profile(const char *name)
13008{
13009 int skip;
13010
13011 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13012 return;
13013 skip = cmdloop(0);
13014 popfile();
13015 if (skip)
13016 exitshell();
13017}
13018
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013019/*
13020 * This routine is called when an error or an interrupt occurs in an
13021 * interactive shell and control is returned to the main command loop.
13022 */
13023static void
13024reset(void)
13025{
13026 /* from eval.c: */
13027 evalskip = 0;
13028 loopnest = 0;
13029 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013030 g_parsefile->left_in_buffer = 0;
13031 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013032 popallfiles();
13033 /* from parser.c: */
13034 tokpushback = 0;
13035 checkkwd = 0;
13036 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013037 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013038}
13039
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013040#if PROFILE
13041static short profile_buf[16384];
13042extern int etext();
13043#endif
13044
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013045/*
13046 * Main routine. We initialize things, parse the arguments, execute
13047 * profiles if we're a login shell, and then call cmdloop to execute
13048 * commands. The setjmp call sets up the location to jump to when an
13049 * exception occurs. When an exception occurs the variable "state"
13050 * is used to figure out how far we had gotten.
13051 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013052int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013053int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013054{
Mike Frysinger98c52642009-04-02 10:02:37 +000013055 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013056 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013057 struct jmploc jmploc;
13058 struct stackmark smark;
13059
Denis Vlasenko01631112007-12-16 17:20:38 +000013060 /* Initialize global data */
13061 INIT_G_misc();
13062 INIT_G_memstack();
13063 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013064#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013065 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013066#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013067 INIT_G_cmdtable();
13068
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013069#if PROFILE
13070 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13071#endif
13072
13073#if ENABLE_FEATURE_EDITING
13074 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13075#endif
13076 state = 0;
13077 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013078 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013079 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013080
13081 reset();
13082
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013083 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013084 if (e == EXERROR)
13085 exitstatus = 2;
13086 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013087 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013088 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013089 }
13090 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013091 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013092 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013093
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013094 popstackmark(&smark);
13095 FORCE_INT_ON; /* enable interrupts */
13096 if (s == 1)
13097 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013098 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013099 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013100 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013101 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013102 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013103 }
13104 exception_handler = &jmploc;
13105#if DEBUG
13106 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013107 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013108 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013109#endif
13110 rootpid = getpid();
13111
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013112 init();
13113 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013114 procargs(argv);
13115
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013116#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13117 if (iflag) {
13118 const char *hp = lookupvar("HISTFILE");
13119
13120 if (hp == NULL) {
13121 hp = lookupvar("HOME");
13122 if (hp != NULL) {
13123 char *defhp = concat_path_file(hp, ".ash_history");
13124 setvar("HISTFILE", defhp, 0);
13125 free(defhp);
13126 }
13127 }
13128 }
13129#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013130 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013131 isloginsh = 1;
13132 if (isloginsh) {
13133 state = 1;
13134 read_profile("/etc/profile");
13135 state1:
13136 state = 2;
13137 read_profile(".profile");
13138 }
13139 state2:
13140 state = 3;
13141 if (
13142#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013143 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013144#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013145 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013146 ) {
13147 shinit = lookupvar("ENV");
13148 if (shinit != NULL && *shinit != '\0') {
13149 read_profile(shinit);
13150 }
13151 }
13152 state3:
13153 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013154 if (minusc) {
13155 /* evalstring pushes parsefile stack.
13156 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013157 * is one of stacked source fds.
13158 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013159 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013160 // ^^ not necessary since now we special-case fd 0
13161 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013162 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013163 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013164
13165 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013166#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013167 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013168 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013169 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013170 line_input_state->hist_file = hp;
13171 }
13172#endif
13173 state4: /* XXX ??? - why isn't this before the "if" statement */
13174 cmdloop(1);
13175 }
13176#if PROFILE
13177 monitor(0);
13178#endif
13179#ifdef GPROF
13180 {
13181 extern void _mcleanup(void);
13182 _mcleanup();
13183 }
13184#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013185 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013186 exitshell();
13187 /* NOTREACHED */
13188}
13189
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013190
Eric Andersendf82f612001-06-28 07:46:40 +000013191/*-
13192 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013193 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013194 *
13195 * This code is derived from software contributed to Berkeley by
13196 * Kenneth Almquist.
13197 *
13198 * Redistribution and use in source and binary forms, with or without
13199 * modification, are permitted provided that the following conditions
13200 * are met:
13201 * 1. Redistributions of source code must retain the above copyright
13202 * notice, this list of conditions and the following disclaimer.
13203 * 2. Redistributions in binary form must reproduce the above copyright
13204 * notice, this list of conditions and the following disclaimer in the
13205 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013206 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013207 * may be used to endorse or promote products derived from this software
13208 * without specific prior written permission.
13209 *
13210 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13211 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13212 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13213 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13214 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13215 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13216 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13217 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13218 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13219 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13220 * SUCH DAMAGE.
13221 */