blob: 09db046fe6e6f93c0bdc349dd26cfea1eed84364 [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;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003542 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003543 jp1 = *jpp;
3544 if (jp1 == jp)
3545 break;
3546 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003547 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548 *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. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003563 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003564 jp1 = *jpp;
3565#if JOBS
3566 if (!jp1 || jp1->state != JOBSTOPPED)
3567#endif
3568 break;
3569 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003570 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003571 /* 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);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003743 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003744 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);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003754 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755 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--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005973 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005974 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;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005991 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005992
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{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007399#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007400 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007401 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007402 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007403 while (*envp)
7404 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007405 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007406 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007407 /* re-exec ourselves with the new arguments */
7408 execve(bb_busybox_exec_path, argv, envp);
7409 /* If they called chroot or otherwise made the binary no longer
7410 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007411 }
7412#endif
7413
7414 repeat:
7415#ifdef SYSV
7416 do {
7417 execve(cmd, argv, envp);
7418 } while (errno == EINTR);
7419#else
7420 execve(cmd, argv, envp);
7421#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007422 if (cmd == (char*) bb_busybox_exec_path) {
7423 /* We already visited ENOEXEC branch below, don't do it again */
7424//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007425 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007426 return;
7427 }
7428 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007429 /* Run "cmd" as a shell script:
7430 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7431 * "If the execve() function fails with ENOEXEC, the shell
7432 * shall execute a command equivalent to having a shell invoked
7433 * with the command name as its first operand,
7434 * with any remaining arguments passed to the new shell"
7435 *
7436 * That is, do not use $SHELL, user's shell, or /bin/sh;
7437 * just call ourselves.
7438 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007439 char **ap;
7440 char **new;
7441
7442 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007443 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007444 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7445 new[0] = (char*) "ash";
7446 new[1] = cmd;
7447 ap = new + 2;
7448 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007449 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007450 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007451 argv = new;
7452 goto repeat;
7453 }
7454}
7455
7456/*
7457 * Exec a program. Never returns. If you change this routine, you may
7458 * have to change the find_command routine as well.
7459 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007460static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007461static void
7462shellexec(char **argv, const char *path, int idx)
7463{
7464 char *cmdname;
7465 int e;
7466 char **envp;
7467 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007468#if ENABLE_FEATURE_SH_STANDALONE
7469 int applet_no = -1;
7470#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007471
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007472 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007473 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007474 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007475#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007476 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007477#endif
7478 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007479 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007480 e = errno;
7481 } else {
7482 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007483 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007484 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007485 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007486 if (errno != ENOENT && errno != ENOTDIR)
7487 e = errno;
7488 }
7489 stunalloc(cmdname);
7490 }
7491 }
7492
7493 /* Map to POSIX errors */
7494 switch (e) {
7495 case EACCES:
7496 exerrno = 126;
7497 break;
7498 case ENOENT:
7499 exerrno = 127;
7500 break;
7501 default:
7502 exerrno = 2;
7503 break;
7504 }
7505 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007506 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7507 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007508 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7509 /* NOTREACHED */
7510}
7511
7512static void
7513printentry(struct tblentry *cmdp)
7514{
7515 int idx;
7516 const char *path;
7517 char *name;
7518
7519 idx = cmdp->param.index;
7520 path = pathval();
7521 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007522 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007523 stunalloc(name);
7524 } while (--idx >= 0);
7525 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7526}
7527
7528/*
7529 * Clear out command entries. The argument specifies the first entry in
7530 * PATH which has changed.
7531 */
7532static void
7533clearcmdentry(int firstchange)
7534{
7535 struct tblentry **tblp;
7536 struct tblentry **pp;
7537 struct tblentry *cmdp;
7538
7539 INT_OFF;
7540 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7541 pp = tblp;
7542 while ((cmdp = *pp) != NULL) {
7543 if ((cmdp->cmdtype == CMDNORMAL &&
7544 cmdp->param.index >= firstchange)
7545 || (cmdp->cmdtype == CMDBUILTIN &&
7546 builtinloc >= firstchange)
7547 ) {
7548 *pp = cmdp->next;
7549 free(cmdp);
7550 } else {
7551 pp = &cmdp->next;
7552 }
7553 }
7554 }
7555 INT_ON;
7556}
7557
7558/*
7559 * Locate a command in the command hash table. If "add" is nonzero,
7560 * add the command to the table if it is not already present. The
7561 * variable "lastcmdentry" is set to point to the address of the link
7562 * pointing to the entry, so that delete_cmd_entry can delete the
7563 * entry.
7564 *
7565 * Interrupts must be off if called with add != 0.
7566 */
7567static struct tblentry **lastcmdentry;
7568
7569static struct tblentry *
7570cmdlookup(const char *name, int add)
7571{
7572 unsigned int hashval;
7573 const char *p;
7574 struct tblentry *cmdp;
7575 struct tblentry **pp;
7576
7577 p = name;
7578 hashval = (unsigned char)*p << 4;
7579 while (*p)
7580 hashval += (unsigned char)*p++;
7581 hashval &= 0x7FFF;
7582 pp = &cmdtable[hashval % CMDTABLESIZE];
7583 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7584 if (strcmp(cmdp->cmdname, name) == 0)
7585 break;
7586 pp = &cmdp->next;
7587 }
7588 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007589 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7590 + strlen(name)
7591 /* + 1 - already done because
7592 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007593 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007594 cmdp->cmdtype = CMDUNKNOWN;
7595 strcpy(cmdp->cmdname, name);
7596 }
7597 lastcmdentry = pp;
7598 return cmdp;
7599}
7600
7601/*
7602 * Delete the command entry returned on the last lookup.
7603 */
7604static void
7605delete_cmd_entry(void)
7606{
7607 struct tblentry *cmdp;
7608
7609 INT_OFF;
7610 cmdp = *lastcmdentry;
7611 *lastcmdentry = cmdp->next;
7612 if (cmdp->cmdtype == CMDFUNCTION)
7613 freefunc(cmdp->param.func);
7614 free(cmdp);
7615 INT_ON;
7616}
7617
7618/*
7619 * Add a new command entry, replacing any existing command entry for
7620 * the same name - except special builtins.
7621 */
7622static void
7623addcmdentry(char *name, struct cmdentry *entry)
7624{
7625 struct tblentry *cmdp;
7626
7627 cmdp = cmdlookup(name, 1);
7628 if (cmdp->cmdtype == CMDFUNCTION) {
7629 freefunc(cmdp->param.func);
7630 }
7631 cmdp->cmdtype = entry->cmdtype;
7632 cmdp->param = entry->u;
7633 cmdp->rehash = 0;
7634}
7635
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007636static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007637hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007638{
7639 struct tblentry **pp;
7640 struct tblentry *cmdp;
7641 int c;
7642 struct cmdentry entry;
7643 char *name;
7644
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007645 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007646 clearcmdentry(0);
7647 return 0;
7648 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007649
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007650 if (*argptr == NULL) {
7651 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7652 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7653 if (cmdp->cmdtype == CMDNORMAL)
7654 printentry(cmdp);
7655 }
7656 }
7657 return 0;
7658 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007659
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007660 c = 0;
7661 while ((name = *argptr) != NULL) {
7662 cmdp = cmdlookup(name, 0);
7663 if (cmdp != NULL
7664 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007665 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7666 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007667 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007668 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007669 find_command(name, &entry, DO_ERR, pathval());
7670 if (entry.cmdtype == CMDUNKNOWN)
7671 c = 1;
7672 argptr++;
7673 }
7674 return c;
7675}
7676
7677/*
7678 * Called when a cd is done. Marks all commands so the next time they
7679 * are executed they will be rehashed.
7680 */
7681static void
7682hashcd(void)
7683{
7684 struct tblentry **pp;
7685 struct tblentry *cmdp;
7686
7687 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7688 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007689 if (cmdp->cmdtype == CMDNORMAL
7690 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007691 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007692 && builtinloc > 0)
7693 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007694 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007695 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007696 }
7697 }
7698}
7699
7700/*
7701 * Fix command hash table when PATH changed.
7702 * Called before PATH is changed. The argument is the new value of PATH;
7703 * pathval() still returns the old value at this point.
7704 * Called with interrupts off.
7705 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007706static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007707changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007708{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007709 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007710 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007711 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007712 int idx_bltin;
7713
7714 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007715 firstchange = 9999; /* assume no change */
7716 idx = 0;
7717 idx_bltin = -1;
7718 for (;;) {
7719 if (*old != *new) {
7720 firstchange = idx;
7721 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007722 || (*old == ':' && *new == '\0')
7723 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007724 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007725 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007726 old = new; /* ignore subsequent differences */
7727 }
7728 if (*new == '\0')
7729 break;
7730 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7731 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007732 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007733 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007734 new++;
7735 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007736 }
7737 if (builtinloc < 0 && idx_bltin >= 0)
7738 builtinloc = idx_bltin; /* zap builtins */
7739 if (builtinloc >= 0 && idx_bltin < 0)
7740 firstchange = 0;
7741 clearcmdentry(firstchange);
7742 builtinloc = idx_bltin;
7743}
7744
7745#define TEOF 0
7746#define TNL 1
7747#define TREDIR 2
7748#define TWORD 3
7749#define TSEMI 4
7750#define TBACKGND 5
7751#define TAND 6
7752#define TOR 7
7753#define TPIPE 8
7754#define TLP 9
7755#define TRP 10
7756#define TENDCASE 11
7757#define TENDBQUOTE 12
7758#define TNOT 13
7759#define TCASE 14
7760#define TDO 15
7761#define TDONE 16
7762#define TELIF 17
7763#define TELSE 18
7764#define TESAC 19
7765#define TFI 20
7766#define TFOR 21
7767#define TIF 22
7768#define TIN 23
7769#define TTHEN 24
7770#define TUNTIL 25
7771#define TWHILE 26
7772#define TBEGIN 27
7773#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007774typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007775
7776/* first char is indicating which tokens mark the end of a list */
7777static const char *const tokname_array[] = {
7778 "\1end of file",
7779 "\0newline",
7780 "\0redirection",
7781 "\0word",
7782 "\0;",
7783 "\0&",
7784 "\0&&",
7785 "\0||",
7786 "\0|",
7787 "\0(",
7788 "\1)",
7789 "\1;;",
7790 "\1`",
7791#define KWDOFFSET 13
7792 /* the following are keywords */
7793 "\0!",
7794 "\0case",
7795 "\1do",
7796 "\1done",
7797 "\1elif",
7798 "\1else",
7799 "\1esac",
7800 "\1fi",
7801 "\0for",
7802 "\0if",
7803 "\0in",
7804 "\1then",
7805 "\0until",
7806 "\0while",
7807 "\0{",
7808 "\1}",
7809};
7810
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007811/* Wrapper around strcmp for qsort/bsearch/... */
7812static int
7813pstrcmp(const void *a, const void *b)
7814{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007815 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007816}
7817
7818static const char *const *
7819findkwd(const char *s)
7820{
7821 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007822 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7823 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007824}
7825
7826/*
7827 * Locate and print what a word is...
7828 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007829static int
7830describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007831{
7832 struct cmdentry entry;
7833 struct tblentry *cmdp;
7834#if ENABLE_ASH_ALIAS
7835 const struct alias *ap;
7836#endif
7837 const char *path = pathval();
7838
7839 if (describe_command_verbose) {
7840 out1str(command);
7841 }
7842
7843 /* First look at the keywords */
7844 if (findkwd(command)) {
7845 out1str(describe_command_verbose ? " is a shell keyword" : command);
7846 goto out;
7847 }
7848
7849#if ENABLE_ASH_ALIAS
7850 /* Then look at the aliases */
7851 ap = lookupalias(command, 0);
7852 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007853 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007854 out1str("alias ");
7855 printalias(ap);
7856 return 0;
7857 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007858 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007859 goto out;
7860 }
7861#endif
7862 /* Then check if it is a tracked alias */
7863 cmdp = cmdlookup(command, 0);
7864 if (cmdp != NULL) {
7865 entry.cmdtype = cmdp->cmdtype;
7866 entry.u = cmdp->param;
7867 } else {
7868 /* Finally use brute force */
7869 find_command(command, &entry, DO_ABS, path);
7870 }
7871
7872 switch (entry.cmdtype) {
7873 case CMDNORMAL: {
7874 int j = entry.u.index;
7875 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007876 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007877 p = command;
7878 } else {
7879 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007880 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007881 stunalloc(p);
7882 } while (--j >= 0);
7883 }
7884 if (describe_command_verbose) {
7885 out1fmt(" is%s %s",
7886 (cmdp ? " a tracked alias for" : nullstr), p
7887 );
7888 } else {
7889 out1str(p);
7890 }
7891 break;
7892 }
7893
7894 case CMDFUNCTION:
7895 if (describe_command_verbose) {
7896 out1str(" is a shell function");
7897 } else {
7898 out1str(command);
7899 }
7900 break;
7901
7902 case CMDBUILTIN:
7903 if (describe_command_verbose) {
7904 out1fmt(" is a %sshell builtin",
7905 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7906 "special " : nullstr
7907 );
7908 } else {
7909 out1str(command);
7910 }
7911 break;
7912
7913 default:
7914 if (describe_command_verbose) {
7915 out1str(": not found\n");
7916 }
7917 return 127;
7918 }
7919 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007920 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007921 return 0;
7922}
7923
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007924static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007925typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007926{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007927 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007928 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007929 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007930
Denis Vlasenko46846e22007-05-20 13:08:31 +00007931 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007932 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007933 i++;
7934 verbose = 0;
7935 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007936 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007937 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007938 }
7939 return err;
7940}
7941
7942#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007943static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007944commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007945{
7946 int c;
7947 enum {
7948 VERIFY_BRIEF = 1,
7949 VERIFY_VERBOSE = 2,
7950 } verify = 0;
7951
7952 while ((c = nextopt("pvV")) != '\0')
7953 if (c == 'V')
7954 verify |= VERIFY_VERBOSE;
7955 else if (c == 'v')
7956 verify |= VERIFY_BRIEF;
7957#if DEBUG
7958 else if (c != 'p')
7959 abort();
7960#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007961 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7962 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007963 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007964 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007965
7966 return 0;
7967}
7968#endif
7969
7970
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007971/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007972
Denis Vlasenko340299a2008-11-21 10:36:36 +00007973static int funcblocksize; /* size of structures in function */
7974static int funcstringsize; /* size of strings in node */
7975static void *funcblock; /* block to allocate function from */
7976static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007977
Eric Andersencb57d552001-06-28 07:25:16 +00007978/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007979#define EV_EXIT 01 /* exit after evaluating tree */
7980#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007981#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007982
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007983static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007984 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7985 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7986 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7987 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7988 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7989 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7990 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7991 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7992 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7993 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7994 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7995 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7996 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7997 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7998 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7999 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8000 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008001#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008002 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008003#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008004 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8005 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8006 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8007 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8008 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8009 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8010 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8011 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8012 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008013};
8014
8015static void calcsize(union node *n);
8016
8017static void
8018sizenodelist(struct nodelist *lp)
8019{
8020 while (lp) {
8021 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8022 calcsize(lp->n);
8023 lp = lp->next;
8024 }
8025}
8026
8027static void
8028calcsize(union node *n)
8029{
8030 if (n == NULL)
8031 return;
8032 funcblocksize += nodesize[n->type];
8033 switch (n->type) {
8034 case NCMD:
8035 calcsize(n->ncmd.redirect);
8036 calcsize(n->ncmd.args);
8037 calcsize(n->ncmd.assign);
8038 break;
8039 case NPIPE:
8040 sizenodelist(n->npipe.cmdlist);
8041 break;
8042 case NREDIR:
8043 case NBACKGND:
8044 case NSUBSHELL:
8045 calcsize(n->nredir.redirect);
8046 calcsize(n->nredir.n);
8047 break;
8048 case NAND:
8049 case NOR:
8050 case NSEMI:
8051 case NWHILE:
8052 case NUNTIL:
8053 calcsize(n->nbinary.ch2);
8054 calcsize(n->nbinary.ch1);
8055 break;
8056 case NIF:
8057 calcsize(n->nif.elsepart);
8058 calcsize(n->nif.ifpart);
8059 calcsize(n->nif.test);
8060 break;
8061 case NFOR:
8062 funcstringsize += strlen(n->nfor.var) + 1;
8063 calcsize(n->nfor.body);
8064 calcsize(n->nfor.args);
8065 break;
8066 case NCASE:
8067 calcsize(n->ncase.cases);
8068 calcsize(n->ncase.expr);
8069 break;
8070 case NCLIST:
8071 calcsize(n->nclist.body);
8072 calcsize(n->nclist.pattern);
8073 calcsize(n->nclist.next);
8074 break;
8075 case NDEFUN:
8076 case NARG:
8077 sizenodelist(n->narg.backquote);
8078 funcstringsize += strlen(n->narg.text) + 1;
8079 calcsize(n->narg.next);
8080 break;
8081 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008082#if ENABLE_ASH_BASH_COMPAT
8083 case NTO2:
8084#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008085 case NCLOBBER:
8086 case NFROM:
8087 case NFROMTO:
8088 case NAPPEND:
8089 calcsize(n->nfile.fname);
8090 calcsize(n->nfile.next);
8091 break;
8092 case NTOFD:
8093 case NFROMFD:
8094 calcsize(n->ndup.vname);
8095 calcsize(n->ndup.next);
8096 break;
8097 case NHERE:
8098 case NXHERE:
8099 calcsize(n->nhere.doc);
8100 calcsize(n->nhere.next);
8101 break;
8102 case NNOT:
8103 calcsize(n->nnot.com);
8104 break;
8105 };
8106}
8107
8108static char *
8109nodeckstrdup(char *s)
8110{
8111 char *rtn = funcstring;
8112
8113 strcpy(funcstring, s);
8114 funcstring += strlen(s) + 1;
8115 return rtn;
8116}
8117
8118static union node *copynode(union node *);
8119
8120static struct nodelist *
8121copynodelist(struct nodelist *lp)
8122{
8123 struct nodelist *start;
8124 struct nodelist **lpp;
8125
8126 lpp = &start;
8127 while (lp) {
8128 *lpp = funcblock;
8129 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8130 (*lpp)->n = copynode(lp->n);
8131 lp = lp->next;
8132 lpp = &(*lpp)->next;
8133 }
8134 *lpp = NULL;
8135 return start;
8136}
8137
8138static union node *
8139copynode(union node *n)
8140{
8141 union node *new;
8142
8143 if (n == NULL)
8144 return NULL;
8145 new = funcblock;
8146 funcblock = (char *) funcblock + nodesize[n->type];
8147
8148 switch (n->type) {
8149 case NCMD:
8150 new->ncmd.redirect = copynode(n->ncmd.redirect);
8151 new->ncmd.args = copynode(n->ncmd.args);
8152 new->ncmd.assign = copynode(n->ncmd.assign);
8153 break;
8154 case NPIPE:
8155 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008156 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008157 break;
8158 case NREDIR:
8159 case NBACKGND:
8160 case NSUBSHELL:
8161 new->nredir.redirect = copynode(n->nredir.redirect);
8162 new->nredir.n = copynode(n->nredir.n);
8163 break;
8164 case NAND:
8165 case NOR:
8166 case NSEMI:
8167 case NWHILE:
8168 case NUNTIL:
8169 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8170 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8171 break;
8172 case NIF:
8173 new->nif.elsepart = copynode(n->nif.elsepart);
8174 new->nif.ifpart = copynode(n->nif.ifpart);
8175 new->nif.test = copynode(n->nif.test);
8176 break;
8177 case NFOR:
8178 new->nfor.var = nodeckstrdup(n->nfor.var);
8179 new->nfor.body = copynode(n->nfor.body);
8180 new->nfor.args = copynode(n->nfor.args);
8181 break;
8182 case NCASE:
8183 new->ncase.cases = copynode(n->ncase.cases);
8184 new->ncase.expr = copynode(n->ncase.expr);
8185 break;
8186 case NCLIST:
8187 new->nclist.body = copynode(n->nclist.body);
8188 new->nclist.pattern = copynode(n->nclist.pattern);
8189 new->nclist.next = copynode(n->nclist.next);
8190 break;
8191 case NDEFUN:
8192 case NARG:
8193 new->narg.backquote = copynodelist(n->narg.backquote);
8194 new->narg.text = nodeckstrdup(n->narg.text);
8195 new->narg.next = copynode(n->narg.next);
8196 break;
8197 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008198#if ENABLE_ASH_BASH_COMPAT
8199 case NTO2:
8200#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008201 case NCLOBBER:
8202 case NFROM:
8203 case NFROMTO:
8204 case NAPPEND:
8205 new->nfile.fname = copynode(n->nfile.fname);
8206 new->nfile.fd = n->nfile.fd;
8207 new->nfile.next = copynode(n->nfile.next);
8208 break;
8209 case NTOFD:
8210 case NFROMFD:
8211 new->ndup.vname = copynode(n->ndup.vname);
8212 new->ndup.dupfd = n->ndup.dupfd;
8213 new->ndup.fd = n->ndup.fd;
8214 new->ndup.next = copynode(n->ndup.next);
8215 break;
8216 case NHERE:
8217 case NXHERE:
8218 new->nhere.doc = copynode(n->nhere.doc);
8219 new->nhere.fd = n->nhere.fd;
8220 new->nhere.next = copynode(n->nhere.next);
8221 break;
8222 case NNOT:
8223 new->nnot.com = copynode(n->nnot.com);
8224 break;
8225 };
8226 new->type = n->type;
8227 return new;
8228}
8229
8230/*
8231 * Make a copy of a parse tree.
8232 */
8233static struct funcnode *
8234copyfunc(union node *n)
8235{
8236 struct funcnode *f;
8237 size_t blocksize;
8238
8239 funcblocksize = offsetof(struct funcnode, n);
8240 funcstringsize = 0;
8241 calcsize(n);
8242 blocksize = funcblocksize;
8243 f = ckmalloc(blocksize + funcstringsize);
8244 funcblock = (char *) f + offsetof(struct funcnode, n);
8245 funcstring = (char *) f + blocksize;
8246 copynode(n);
8247 f->count = 0;
8248 return f;
8249}
8250
8251/*
8252 * Define a shell function.
8253 */
8254static void
8255defun(char *name, union node *func)
8256{
8257 struct cmdentry entry;
8258
8259 INT_OFF;
8260 entry.cmdtype = CMDFUNCTION;
8261 entry.u.func = copyfunc(func);
8262 addcmdentry(name, &entry);
8263 INT_ON;
8264}
8265
Denis Vlasenko4b875702009-03-19 13:30:04 +00008266/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008267#define SKIPBREAK (1 << 0)
8268#define SKIPCONT (1 << 1)
8269#define SKIPFUNC (1 << 2)
8270#define SKIPFILE (1 << 3)
8271#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008272static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008273static int skipcount; /* number of levels to skip */
8274static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008275static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008276
Denis Vlasenko4b875702009-03-19 13:30:04 +00008277/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008278static int evalstring(char *s, int mask);
8279
Denis Vlasenko4b875702009-03-19 13:30:04 +00008280/* Called to execute a trap.
8281 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008282 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008283 *
8284 * Perhaps we should avoid entering new trap handlers
8285 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008286 */
8287static int
8288dotrap(void)
8289{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008290 uint8_t *g;
8291 int sig;
8292 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008293
8294 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008295 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008296 xbarrier();
8297
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008298 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008299 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8300 int want_exexit;
8301 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008302
Denis Vlasenko4b875702009-03-19 13:30:04 +00008303 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008304 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008305 t = trap[sig];
8306 /* non-trapped SIGINT is handled separately by raise_interrupt,
8307 * don't upset it by resetting gotsig[SIGINT-1] */
8308 if (sig == SIGINT && !t)
8309 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008310
8311 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008312 *g = 0;
8313 if (!t)
8314 continue;
8315 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008316 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008317 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008318 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008319 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008320 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008321 }
8322
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008323 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008324 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008325}
8326
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008327/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008328static void evalloop(union node *, int);
8329static void evalfor(union node *, int);
8330static void evalcase(union node *, int);
8331static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008332static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008333static void evalpipe(union node *, int);
8334static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008335static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008336static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008337
Eric Andersen62483552001-07-10 06:09:16 +00008338/*
Eric Andersenc470f442003-07-28 09:56:35 +00008339 * Evaluate a parse tree. The value is left in the global variable
8340 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008341 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008342static void
Eric Andersenc470f442003-07-28 09:56:35 +00008343evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008344{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008345 struct jmploc *volatile savehandler = exception_handler;
8346 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008347 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008348 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008349 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008350 int int_level;
8351
8352 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008353
Eric Andersenc470f442003-07-28 09:56:35 +00008354 if (n == NULL) {
8355 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008356 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008357 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008358 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008359
8360 exception_handler = &jmploc;
8361 {
8362 int err = setjmp(jmploc.loc);
8363 if (err) {
8364 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008365 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008366 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8367 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008368 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008369 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008370 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008371 TRACE(("exception %d in evaltree, propagating err=%d\n",
8372 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008373 exception_handler = savehandler;
8374 longjmp(exception_handler->loc, err);
8375 }
8376 }
8377
Eric Andersenc470f442003-07-28 09:56:35 +00008378 switch (n->type) {
8379 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008380#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008381 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008382 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008383 break;
8384#endif
8385 case NNOT:
8386 evaltree(n->nnot.com, EV_TESTED);
8387 status = !exitstatus;
8388 goto setstatus;
8389 case NREDIR:
8390 expredir(n->nredir.redirect);
8391 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8392 if (!status) {
8393 evaltree(n->nredir.n, flags & EV_TESTED);
8394 status = exitstatus;
8395 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008396 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008397 goto setstatus;
8398 case NCMD:
8399 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008400 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008401 if (eflag && !(flags & EV_TESTED))
8402 checkexit = ~0;
8403 goto calleval;
8404 case NFOR:
8405 evalfn = evalfor;
8406 goto calleval;
8407 case NWHILE:
8408 case NUNTIL:
8409 evalfn = evalloop;
8410 goto calleval;
8411 case NSUBSHELL:
8412 case NBACKGND:
8413 evalfn = evalsubshell;
8414 goto calleval;
8415 case NPIPE:
8416 evalfn = evalpipe;
8417 goto checkexit;
8418 case NCASE:
8419 evalfn = evalcase;
8420 goto calleval;
8421 case NAND:
8422 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008423 case NSEMI: {
8424
Eric Andersenc470f442003-07-28 09:56:35 +00008425#if NAND + 1 != NOR
8426#error NAND + 1 != NOR
8427#endif
8428#if NOR + 1 != NSEMI
8429#error NOR + 1 != NSEMI
8430#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008431 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008432 evaltree(
8433 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008434 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008435 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008436 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008437 break;
8438 if (!evalskip) {
8439 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008440 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008441 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008442 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008443 evalfn(n, flags);
8444 break;
8445 }
8446 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008447 }
Eric Andersenc470f442003-07-28 09:56:35 +00008448 case NIF:
8449 evaltree(n->nif.test, EV_TESTED);
8450 if (evalskip)
8451 break;
8452 if (exitstatus == 0) {
8453 n = n->nif.ifpart;
8454 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008455 }
8456 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008457 n = n->nif.elsepart;
8458 goto evaln;
8459 }
8460 goto success;
8461 case NDEFUN:
8462 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008463 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008464 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008465 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008466 exitstatus = status;
8467 break;
8468 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008469
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008470 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008471 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008472
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008473 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008474 /* Order of checks below is important:
8475 * signal handlers trigger before exit caused by "set -e".
8476 */
8477 if (pending_sig && dotrap())
8478 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008479 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008480 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008481
8482 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008483 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008484 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008485 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008486
8487 RESTORE_INT(int_level);
8488 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008489}
8490
Eric Andersenc470f442003-07-28 09:56:35 +00008491#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8492static
8493#endif
8494void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8495
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008496static void
Eric Andersenc470f442003-07-28 09:56:35 +00008497evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008498{
8499 int status;
8500
8501 loopnest++;
8502 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008503 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008504 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008505 int i;
8506
Eric Andersencb57d552001-06-28 07:25:16 +00008507 evaltree(n->nbinary.ch1, EV_TESTED);
8508 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008509 skipping:
8510 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008511 evalskip = 0;
8512 continue;
8513 }
8514 if (evalskip == SKIPBREAK && --skipcount <= 0)
8515 evalskip = 0;
8516 break;
8517 }
Eric Andersenc470f442003-07-28 09:56:35 +00008518 i = exitstatus;
8519 if (n->type != NWHILE)
8520 i = !i;
8521 if (i != 0)
8522 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008523 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008524 status = exitstatus;
8525 if (evalskip)
8526 goto skipping;
8527 }
8528 loopnest--;
8529 exitstatus = status;
8530}
8531
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008532static void
Eric Andersenc470f442003-07-28 09:56:35 +00008533evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008534{
8535 struct arglist arglist;
8536 union node *argp;
8537 struct strlist *sp;
8538 struct stackmark smark;
8539
8540 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008541 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008542 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008543 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008544 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008545 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008546 if (evalskip)
8547 goto out;
8548 }
8549 *arglist.lastp = NULL;
8550
8551 exitstatus = 0;
8552 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008553 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008554 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008555 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008556 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008557 if (evalskip) {
8558 if (evalskip == SKIPCONT && --skipcount <= 0) {
8559 evalskip = 0;
8560 continue;
8561 }
8562 if (evalskip == SKIPBREAK && --skipcount <= 0)
8563 evalskip = 0;
8564 break;
8565 }
8566 }
8567 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008568 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008569 popstackmark(&smark);
8570}
8571
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008572static void
Eric Andersenc470f442003-07-28 09:56:35 +00008573evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008574{
8575 union node *cp;
8576 union node *patp;
8577 struct arglist arglist;
8578 struct stackmark smark;
8579
8580 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008581 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008582 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008583 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008584 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008585 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8586 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008587 if (casematch(patp, arglist.list->text)) {
8588 if (evalskip == 0) {
8589 evaltree(cp->nclist.body, flags);
8590 }
8591 goto out;
8592 }
8593 }
8594 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008595 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008596 popstackmark(&smark);
8597}
8598
Eric Andersenc470f442003-07-28 09:56:35 +00008599/*
8600 * Kick off a subshell to evaluate a tree.
8601 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008602static void
Eric Andersenc470f442003-07-28 09:56:35 +00008603evalsubshell(union node *n, int flags)
8604{
8605 struct job *jp;
8606 int backgnd = (n->type == NBACKGND);
8607 int status;
8608
8609 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008610 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008611 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008612 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008613 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008614 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008615 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008616 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008617 flags |= EV_EXIT;
8618 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008619 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008620 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008621 redirect(n->nredir.redirect, 0);
8622 evaltreenr(n->nredir.n, flags);
8623 /* never returns */
8624 }
8625 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008626 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008627 status = waitforjob(jp);
8628 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008629 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008630}
8631
Eric Andersenc470f442003-07-28 09:56:35 +00008632/*
8633 * Compute the names of the files in a redirection list.
8634 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008635static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008636static void
8637expredir(union node *n)
8638{
8639 union node *redir;
8640
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008641 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008642 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008643
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008644 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008645 fn.lastp = &fn.list;
8646 switch (redir->type) {
8647 case NFROMTO:
8648 case NFROM:
8649 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008650#if ENABLE_ASH_BASH_COMPAT
8651 case NTO2:
8652#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008653 case NCLOBBER:
8654 case NAPPEND:
8655 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008656#if ENABLE_ASH_BASH_COMPAT
8657 store_expfname:
8658#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008659 redir->nfile.expfname = fn.list->text;
8660 break;
8661 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008662 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008663 if (redir->ndup.vname) {
8664 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008665 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008666 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008667#if ENABLE_ASH_BASH_COMPAT
8668//FIXME: we used expandarg with different args!
8669 if (!isdigit_str9(fn.list->text)) {
8670 /* >&file, not >&fd */
8671 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8672 ash_msg_and_raise_error("redir error");
8673 redir->type = NTO2;
8674 goto store_expfname;
8675 }
8676#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008677 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008678 }
8679 break;
8680 }
8681 }
8682}
8683
Eric Andersencb57d552001-06-28 07:25:16 +00008684/*
Eric Andersencb57d552001-06-28 07:25:16 +00008685 * Evaluate a pipeline. All the processes in the pipeline are children
8686 * of the process creating the pipeline. (This differs from some versions
8687 * of the shell, which make the last process in a pipeline the parent
8688 * of all the rest.)
8689 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008690static void
Eric Andersenc470f442003-07-28 09:56:35 +00008691evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008692{
8693 struct job *jp;
8694 struct nodelist *lp;
8695 int pipelen;
8696 int prevfd;
8697 int pip[2];
8698
Eric Andersenc470f442003-07-28 09:56:35 +00008699 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008700 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008701 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008702 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008703 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008704 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008705 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008706 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008707 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008708 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008709 pip[1] = -1;
8710 if (lp->next) {
8711 if (pipe(pip) < 0) {
8712 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008713 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008714 }
8715 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008716 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008717 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008718 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008719 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008720 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008721 if (prevfd > 0) {
8722 dup2(prevfd, 0);
8723 close(prevfd);
8724 }
8725 if (pip[1] > 1) {
8726 dup2(pip[1], 1);
8727 close(pip[1]);
8728 }
Eric Andersenc470f442003-07-28 09:56:35 +00008729 evaltreenr(lp->n, flags);
8730 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008731 }
8732 if (prevfd >= 0)
8733 close(prevfd);
8734 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008735 /* Don't want to trigger debugging */
8736 if (pip[1] != -1)
8737 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008738 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008739 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008740 exitstatus = waitforjob(jp);
8741 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008742 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008743 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008744}
8745
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008746/*
8747 * Controls whether the shell is interactive or not.
8748 */
8749static void
8750setinteractive(int on)
8751{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008752 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008753
8754 if (++on == is_interactive)
8755 return;
8756 is_interactive = on;
8757 setsignal(SIGINT);
8758 setsignal(SIGQUIT);
8759 setsignal(SIGTERM);
8760#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8761 if (is_interactive > 1) {
8762 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008763 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008764
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008765 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008766 /* note: ash and hush share this string */
8767 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008768 "Enter 'help' for a list of built-in commands."
8769 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008770 bb_banner,
8771 "built-in shell (ash)"
8772 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008773 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008774 }
8775 }
8776#endif
8777}
8778
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008779static void
8780optschanged(void)
8781{
8782#if DEBUG
8783 opentrace();
8784#endif
8785 setinteractive(iflag);
8786 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008787#if ENABLE_FEATURE_EDITING_VI
8788 if (viflag)
8789 line_input_state->flags |= VI_MODE;
8790 else
8791 line_input_state->flags &= ~VI_MODE;
8792#else
8793 viflag = 0; /* forcibly keep the option off */
8794#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008795}
8796
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008797static struct localvar *localvars;
8798
8799/*
8800 * Called after a function returns.
8801 * Interrupts must be off.
8802 */
8803static void
8804poplocalvars(void)
8805{
8806 struct localvar *lvp;
8807 struct var *vp;
8808
8809 while ((lvp = localvars) != NULL) {
8810 localvars = lvp->next;
8811 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008812 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008813 if (vp == NULL) { /* $- saved */
8814 memcpy(optlist, lvp->text, sizeof(optlist));
8815 free((char*)lvp->text);
8816 optschanged();
8817 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008818 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008820 if (vp->var_func)
8821 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008822 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008823 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008824 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008825 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008826 }
8827 free(lvp);
8828 }
8829}
8830
8831static int
8832evalfun(struct funcnode *func, int argc, char **argv, int flags)
8833{
8834 volatile struct shparam saveparam;
8835 struct localvar *volatile savelocalvars;
8836 struct jmploc *volatile savehandler;
8837 struct jmploc jmploc;
8838 int e;
8839
8840 saveparam = shellparam;
8841 savelocalvars = localvars;
8842 e = setjmp(jmploc.loc);
8843 if (e) {
8844 goto funcdone;
8845 }
8846 INT_OFF;
8847 savehandler = exception_handler;
8848 exception_handler = &jmploc;
8849 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008850 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008851 func->count++;
8852 funcnest++;
8853 INT_ON;
8854 shellparam.nparam = argc - 1;
8855 shellparam.p = argv + 1;
8856#if ENABLE_ASH_GETOPTS
8857 shellparam.optind = 1;
8858 shellparam.optoff = -1;
8859#endif
8860 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008861 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008862 INT_OFF;
8863 funcnest--;
8864 freefunc(func);
8865 poplocalvars();
8866 localvars = savelocalvars;
8867 freeparam(&shellparam);
8868 shellparam = saveparam;
8869 exception_handler = savehandler;
8870 INT_ON;
8871 evalskip &= ~SKIPFUNC;
8872 return e;
8873}
8874
Denis Vlasenko131ae172007-02-18 13:00:19 +00008875#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008876static char **
8877parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008878{
8879 char *cp, c;
8880
8881 for (;;) {
8882 cp = *++argv;
8883 if (!cp)
8884 return 0;
8885 if (*cp++ != '-')
8886 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008887 c = *cp++;
8888 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008889 break;
8890 if (c == '-' && !*cp) {
8891 argv++;
8892 break;
8893 }
8894 do {
8895 switch (c) {
8896 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008897 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008898 break;
8899 default:
8900 /* run 'typecmd' for other options */
8901 return 0;
8902 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008903 c = *cp++;
8904 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008905 }
8906 return argv;
8907}
8908#endif
8909
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008910/*
8911 * Make a variable a local variable. When a variable is made local, it's
8912 * value and flags are saved in a localvar structure. The saved values
8913 * will be restored when the shell function returns. We handle the name
8914 * "-" as a special case.
8915 */
8916static void
8917mklocal(char *name)
8918{
8919 struct localvar *lvp;
8920 struct var **vpp;
8921 struct var *vp;
8922
8923 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008924 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008925 if (LONE_DASH(name)) {
8926 char *p;
8927 p = ckmalloc(sizeof(optlist));
8928 lvp->text = memcpy(p, optlist, sizeof(optlist));
8929 vp = NULL;
8930 } else {
8931 char *eq;
8932
8933 vpp = hashvar(name);
8934 vp = *findvar(vpp, name);
8935 eq = strchr(name, '=');
8936 if (vp == NULL) {
8937 if (eq)
8938 setvareq(name, VSTRFIXED);
8939 else
8940 setvar(name, NULL, VSTRFIXED);
8941 vp = *vpp; /* the new variable */
8942 lvp->flags = VUNSET;
8943 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008944 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008945 lvp->flags = vp->flags;
8946 vp->flags |= VSTRFIXED|VTEXTFIXED;
8947 if (eq)
8948 setvareq(name, 0);
8949 }
8950 }
8951 lvp->vp = vp;
8952 lvp->next = localvars;
8953 localvars = lvp;
8954 INT_ON;
8955}
8956
8957/*
8958 * The "local" command.
8959 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008960static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008961localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008962{
8963 char *name;
8964
8965 argv = argptr;
8966 while ((name = *argv++) != NULL) {
8967 mklocal(name);
8968 }
8969 return 0;
8970}
8971
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008972static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008973falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008974{
8975 return 1;
8976}
8977
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008978static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008979truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008980{
8981 return 0;
8982}
8983
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008984static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008985execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008986{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008987 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008988 iflag = 0; /* exit on error */
8989 mflag = 0;
8990 optschanged();
8991 shellexec(argv + 1, pathval(), 0);
8992 }
8993 return 0;
8994}
8995
8996/*
8997 * The return command.
8998 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008999static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009000returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009001{
9002 /*
9003 * If called outside a function, do what ksh does;
9004 * skip the rest of the file.
9005 */
9006 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9007 return argv[1] ? number(argv[1]) : exitstatus;
9008}
9009
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009010/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009011static int breakcmd(int, char **) FAST_FUNC;
9012static int dotcmd(int, char **) FAST_FUNC;
9013static int evalcmd(int, char **) FAST_FUNC;
9014static int exitcmd(int, char **) FAST_FUNC;
9015static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009016#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009017static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009018#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009019#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009020static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009021#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009022#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009023static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009024#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009025static int readcmd(int, char **) FAST_FUNC;
9026static int setcmd(int, char **) FAST_FUNC;
9027static int shiftcmd(int, char **) FAST_FUNC;
9028static int timescmd(int, char **) FAST_FUNC;
9029static int trapcmd(int, char **) FAST_FUNC;
9030static int umaskcmd(int, char **) FAST_FUNC;
9031static int unsetcmd(int, char **) FAST_FUNC;
9032static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009033
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009034#define BUILTIN_NOSPEC "0"
9035#define BUILTIN_SPECIAL "1"
9036#define BUILTIN_REGULAR "2"
9037#define BUILTIN_SPEC_REG "3"
9038#define BUILTIN_ASSIGN "4"
9039#define BUILTIN_SPEC_ASSG "5"
9040#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009041#define BUILTIN_SPEC_REG_ASSG "7"
9042
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009043/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009044#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009045static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009046#endif
9047#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009048static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009049#endif
9050#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009051static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009052#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009053
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009054/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009056 { BUILTIN_SPEC_REG "." , dotcmd },
9057 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009058#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009059 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009060#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009061 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009062#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009063#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009064#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009065 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009066#endif
9067#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009068 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009069#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009070 { BUILTIN_SPEC_REG "break" , breakcmd },
9071 { BUILTIN_REGULAR "cd" , cdcmd },
9072 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009073#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009074 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009075#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009076 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009077#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009078 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009079#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009080 { BUILTIN_SPEC_REG "eval" , evalcmd },
9081 { BUILTIN_SPEC_REG "exec" , execcmd },
9082 { BUILTIN_SPEC_REG "exit" , exitcmd },
9083 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9084 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009085#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009086 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#endif
9088#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009089 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009090#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009091 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009092#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009093 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009094#endif
9095#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009096 { BUILTIN_REGULAR "jobs" , jobscmd },
9097 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009098#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009099#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009100 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009101#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009102 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009103#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009104 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009105#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009106 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9107 { BUILTIN_REGULAR "read" , readcmd },
9108 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9109 { BUILTIN_SPEC_REG "return" , returncmd },
9110 { BUILTIN_SPEC_REG "set" , setcmd },
9111 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009112#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009113 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009114#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009115#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009116 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009117#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_SPEC_REG "times" , timescmd },
9119 { BUILTIN_SPEC_REG "trap" , trapcmd },
9120 { BUILTIN_REGULAR "true" , truecmd },
9121 { BUILTIN_NOSPEC "type" , typecmd },
9122 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9123 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009124#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009125 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009126#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009127 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9128 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009129};
9130
Denis Vlasenko80591b02008-03-25 07:49:43 +00009131/* Should match the above table! */
9132#define COMMANDCMD (builtintab + \
9133 2 + \
9134 1 * ENABLE_ASH_BUILTIN_TEST + \
9135 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9136 1 * ENABLE_ASH_ALIAS + \
9137 1 * ENABLE_ASH_JOB_CONTROL + \
9138 3)
9139#define EXECCMD (builtintab + \
9140 2 + \
9141 1 * ENABLE_ASH_BUILTIN_TEST + \
9142 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9143 1 * ENABLE_ASH_ALIAS + \
9144 1 * ENABLE_ASH_JOB_CONTROL + \
9145 3 + \
9146 1 * ENABLE_ASH_CMDCMD + \
9147 1 + \
9148 ENABLE_ASH_BUILTIN_ECHO + \
9149 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009150
9151/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009152 * Search the table of builtin commands.
9153 */
9154static struct builtincmd *
9155find_builtin(const char *name)
9156{
9157 struct builtincmd *bp;
9158
9159 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009160 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009161 pstrcmp
9162 );
9163 return bp;
9164}
9165
9166/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009167 * Execute a simple command.
9168 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009169static int
9170isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009171{
9172 const char *q = endofname(p);
9173 if (p == q)
9174 return 0;
9175 return *q == '=';
9176}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009177static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009178bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009179{
9180 /* Preserve exitstatus of a previous possible redirection
9181 * as POSIX mandates */
9182 return back_exitstatus;
9183}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009184static void
Eric Andersenc470f442003-07-28 09:56:35 +00009185evalcommand(union node *cmd, int flags)
9186{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009187 static const struct builtincmd null_bltin = {
9188 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009189 };
Eric Andersenc470f442003-07-28 09:56:35 +00009190 struct stackmark smark;
9191 union node *argp;
9192 struct arglist arglist;
9193 struct arglist varlist;
9194 char **argv;
9195 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009196 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009197 struct cmdentry cmdentry;
9198 struct job *jp;
9199 char *lastarg;
9200 const char *path;
9201 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009202 int status;
9203 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009204 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009205 smallint cmd_is_exec;
9206 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009207
9208 /* First expand the arguments. */
9209 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9210 setstackmark(&smark);
9211 back_exitstatus = 0;
9212
9213 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009214 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009215 varlist.lastp = &varlist.list;
9216 *varlist.lastp = NULL;
9217 arglist.lastp = &arglist.list;
9218 *arglist.lastp = NULL;
9219
9220 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009221 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009222 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9223 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9224 }
9225
Eric Andersenc470f442003-07-28 09:56:35 +00009226 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9227 struct strlist **spp;
9228
9229 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009230 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009231 expandarg(argp, &arglist, EXP_VARTILDE);
9232 else
9233 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9234
Eric Andersenc470f442003-07-28 09:56:35 +00009235 for (sp = *spp; sp; sp = sp->next)
9236 argc++;
9237 }
9238
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009239 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009240 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009241 TRACE(("evalcommand arg: %s\n", sp->text));
9242 *nargv++ = sp->text;
9243 }
9244 *nargv = NULL;
9245
9246 lastarg = NULL;
9247 if (iflag && funcnest == 0 && argc > 0)
9248 lastarg = nargv[-1];
9249
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009250 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009251 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009252 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009253
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009254 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009255 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9256 struct strlist **spp;
9257 char *p;
9258
9259 spp = varlist.lastp;
9260 expandarg(argp, &varlist, EXP_VARTILDE);
9261
9262 /*
9263 * Modify the command lookup path, if a PATH= assignment
9264 * is present
9265 */
9266 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009267 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009268 path = p;
9269 }
9270
9271 /* Print the command if xflag is set. */
9272 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009273 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009274 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009275
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009276 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009277 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009278 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009279 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009280 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009281 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009282 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009283 }
9284 sp = arglist.list;
9285 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009286 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009287 }
9288
9289 cmd_is_exec = 0;
9290 spclbltin = -1;
9291
9292 /* Now locate the command. */
9293 if (argc) {
9294 const char *oldpath;
9295 int cmd_flag = DO_ERR;
9296
9297 path += 5;
9298 oldpath = path;
9299 for (;;) {
9300 find_command(argv[0], &cmdentry, cmd_flag, path);
9301 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009302 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009303 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009304 goto bail;
9305 }
9306
9307 /* implement bltin and command here */
9308 if (cmdentry.cmdtype != CMDBUILTIN)
9309 break;
9310 if (spclbltin < 0)
9311 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9312 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009313 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009314#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009315 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009316 path = oldpath;
9317 nargv = parse_command_args(argv, &path);
9318 if (!nargv)
9319 break;
9320 argc -= nargv - argv;
9321 argv = nargv;
9322 cmd_flag |= DO_NOFUNC;
9323 } else
9324#endif
9325 break;
9326 }
9327 }
9328
9329 if (status) {
9330 /* We have a redirection error. */
9331 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009332 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009333 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009334 exitstatus = status;
9335 goto out;
9336 }
9337
9338 /* Execute the command. */
9339 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009340 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009341
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009342#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009343/* (1) BUG: if variables are set, we need to fork, or save/restore them
9344 * around run_nofork_applet() call.
9345 * (2) Should this check also be done in forkshell()?
9346 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9347 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009348 /* find_command() encodes applet_no as (-2 - applet_no) */
9349 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009350 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009351 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009352 /* run <applet>_main() */
9353 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009354 break;
9355 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009356#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009357 /* Can we avoid forking off? For example, very last command
9358 * in a script or a subshell does not need forking,
9359 * we can just exec it.
9360 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009361 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009362 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009363 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009364 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009365 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009366 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009367 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009368 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009369 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009370 break;
9371 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009372 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009373 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009374 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009375 }
9376 listsetvar(varlist.list, VEXPORT|VSTACK);
9377 shellexec(argv, path, cmdentry.u.index);
9378 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009379 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009380 case CMDBUILTIN:
9381 cmdenviron = varlist.list;
9382 if (cmdenviron) {
9383 struct strlist *list = cmdenviron;
9384 int i = VNOSET;
9385 if (spclbltin > 0 || argc == 0) {
9386 i = 0;
9387 if (cmd_is_exec && argc > 1)
9388 i = VEXPORT;
9389 }
9390 listsetvar(list, i);
9391 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009392 /* Tight loop with builtins only:
9393 * "while kill -0 $child; do true; done"
9394 * will never exit even if $child died, unless we do this
9395 * to reap the zombie and make kill detect that it's gone: */
9396 dowait(DOWAIT_NONBLOCK, NULL);
9397
Eric Andersenc470f442003-07-28 09:56:35 +00009398 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9399 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009400 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009401 if (i == EXEXIT)
9402 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009403 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009404 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009405 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009406 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009407 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009408 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009409 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009410 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009411 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009412 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009413 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009414 }
9415 break;
9416
9417 case CMDFUNCTION:
9418 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009419 /* See above for the rationale */
9420 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009421 if (evalfun(cmdentry.u.func, argc, argv, flags))
9422 goto raise;
9423 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009424
9425 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009426
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009427 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009428 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009429 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009430 /* dsl: I think this is intended to be used to support
9431 * '_' in 'vi' command mode during line editing...
9432 * However I implemented that within libedit itself.
9433 */
9434 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009435 }
Eric Andersenc470f442003-07-28 09:56:35 +00009436 popstackmark(&smark);
9437}
9438
9439static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009440evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9441{
Eric Andersenc470f442003-07-28 09:56:35 +00009442 char *volatile savecmdname;
9443 struct jmploc *volatile savehandler;
9444 struct jmploc jmploc;
9445 int i;
9446
9447 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009448 i = setjmp(jmploc.loc);
9449 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009450 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009451 savehandler = exception_handler;
9452 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009453 commandname = argv[0];
9454 argptr = argv + 1;
9455 optptr = NULL; /* initialize nextopt */
9456 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009457 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009458 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009459 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009460 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009461 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009462 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009463
9464 return i;
9465}
9466
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009467static int
9468goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009469{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009470 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009471}
9472
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009473
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009474/*
9475 * Search for a command. This is called before we fork so that the
9476 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009477 * the child. The check for "goodname" is an overly conservative
9478 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009479 */
Eric Andersenc470f442003-07-28 09:56:35 +00009480static void
9481prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009482{
9483 struct cmdentry entry;
9484
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009485 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9486 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009487}
9488
Eric Andersencb57d552001-06-28 07:25:16 +00009489
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009490/* ============ Builtin commands
9491 *
9492 * Builtin commands whose functions are closely tied to evaluation
9493 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009494 */
9495
9496/*
Eric Andersencb57d552001-06-28 07:25:16 +00009497 * Handle break and continue commands. Break, continue, and return are
9498 * all handled by setting the evalskip flag. The evaluation routines
9499 * above all check this flag, and if it is set they start skipping
9500 * commands rather than executing them. The variable skipcount is
9501 * the number of loops to break/continue, or the number of function
9502 * levels to return. (The latter is always 1.) It should probably
9503 * be an error to break out of more loops than exist, but it isn't
9504 * in the standard shell so we don't make it one here.
9505 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009506static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009507breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009508{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009509 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009510
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009511 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009512 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009513 if (n > loopnest)
9514 n = loopnest;
9515 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009516 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009517 skipcount = n;
9518 }
9519 return 0;
9520}
9521
Eric Andersenc470f442003-07-28 09:56:35 +00009522
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009523/* ============ input.c
9524 *
Eric Andersen90898442003-08-06 11:20:52 +00009525 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009526 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009527
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009528enum {
9529 INPUT_PUSH_FILE = 1,
9530 INPUT_NOFILE_OK = 2,
9531};
Eric Andersencb57d552001-06-28 07:25:16 +00009532
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009533static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009534/* values of checkkwd variable */
9535#define CHKALIAS 0x1
9536#define CHKKWD 0x2
9537#define CHKNL 0x4
9538
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009539/*
9540 * Push a string back onto the input at this current parsefile level.
9541 * We handle aliases this way.
9542 */
9543#if !ENABLE_ASH_ALIAS
9544#define pushstring(s, ap) pushstring(s)
9545#endif
9546static void
9547pushstring(char *s, struct alias *ap)
9548{
9549 struct strpush *sp;
9550 int len;
9551
9552 len = strlen(s);
9553 INT_OFF;
9554 if (g_parsefile->strpush) {
9555 sp = ckzalloc(sizeof(*sp));
9556 sp->prev = g_parsefile->strpush;
9557 } else {
9558 sp = &(g_parsefile->basestrpush);
9559 }
9560 g_parsefile->strpush = sp;
9561 sp->prev_string = g_parsefile->next_to_pgetc;
9562 sp->prev_left_in_line = g_parsefile->left_in_line;
9563#if ENABLE_ASH_ALIAS
9564 sp->ap = ap;
9565 if (ap) {
9566 ap->flag |= ALIASINUSE;
9567 sp->string = s;
9568 }
9569#endif
9570 g_parsefile->next_to_pgetc = s;
9571 g_parsefile->left_in_line = len;
9572 INT_ON;
9573}
9574
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009575static void
9576popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009577{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009578 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009579
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009580 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009581#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009582 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009583 if (g_parsefile->next_to_pgetc[-1] == ' '
9584 || g_parsefile->next_to_pgetc[-1] == '\t'
9585 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009586 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009587 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009588 if (sp->string != sp->ap->val) {
9589 free(sp->string);
9590 }
9591 sp->ap->flag &= ~ALIASINUSE;
9592 if (sp->ap->flag & ALIASDEAD) {
9593 unalias(sp->ap->name);
9594 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009595 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009596#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009597 g_parsefile->next_to_pgetc = sp->prev_string;
9598 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009599 g_parsefile->strpush = sp->prev;
9600 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009601 free(sp);
9602 INT_ON;
9603}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009604
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009605//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9606//it peeks whether it is &>, and then pushes back both chars.
9607//This function needs to save last *next_to_pgetc to buf[0]
9608//to make two pungetc() reliable. Currently,
9609// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009610static int
9611preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009612{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009613 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009614 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009615
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009616 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009617#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009618 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009619 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9620 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009621 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009622 int timeout = -1;
9623# if ENABLE_ASH_IDLE_TIMEOUT
9624 if (iflag) {
9625 const char *tmout_var = lookupvar("TMOUT");
9626 if (tmout_var) {
9627 timeout = atoi(tmout_var) * 1000;
9628 if (timeout <= 0)
9629 timeout = -1;
9630 }
9631 }
9632# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009633# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009634 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009635# endif
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009636 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009637 if (nr == 0) {
9638 /* Ctrl+C pressed */
9639 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009640 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009641 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009642 raise(SIGINT);
9643 return 1;
9644 }
Eric Andersenc470f442003-07-28 09:56:35 +00009645 goto retry;
9646 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009647 if (nr < 0) {
9648 if (errno == 0) {
9649 /* Ctrl+D pressed */
9650 nr = 0;
9651 }
9652# if ENABLE_ASH_IDLE_TIMEOUT
9653 else if (errno == EAGAIN && timeout > 0) {
9654 printf("\007timed out waiting for input: auto-logout\n");
9655 exitshell();
9656 }
9657# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009658 }
Eric Andersencb57d552001-06-28 07:25:16 +00009659 }
9660#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009661 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009662#endif
9663
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009664#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009665 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009666 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009667 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009668 if (flags >= 0 && (flags & O_NONBLOCK)) {
9669 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009670 if (fcntl(0, F_SETFL, flags) >= 0) {
9671 out2str("sh: turning off NDELAY mode\n");
9672 goto retry;
9673 }
9674 }
9675 }
9676 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009677#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009678 return nr;
9679}
9680
9681/*
9682 * Refill the input buffer and return the next input character:
9683 *
9684 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009685 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9686 * or we are reading from a string so we can't refill the buffer,
9687 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009688 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009689 * 4) Process input up to the next newline, deleting nul characters.
9690 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009691//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9692#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009693static int
Eric Andersenc470f442003-07-28 09:56:35 +00009694preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009695{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009696 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009697 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009698
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009699 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009700#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009701 if (g_parsefile->left_in_line == -1
9702 && g_parsefile->strpush->ap
9703 && g_parsefile->next_to_pgetc[-1] != ' '
9704 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009705 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009706 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009707 return PEOA;
9708 }
Eric Andersen2870d962001-07-02 17:27:21 +00009709#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009710 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009711 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9713 g_parsefile->left_in_line,
9714 g_parsefile->next_to_pgetc,
9715 g_parsefile->next_to_pgetc);
9716 if (--g_parsefile->left_in_line >= 0)
9717 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009718 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009719 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009720 * "pgetc" needs refilling.
9721 */
9722
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009723 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009724 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009725 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009726 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009727 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009729 /* even in failure keep left_in_line and next_to_pgetc
9730 * in lock step, for correct multi-layer pungetc.
9731 * left_in_line was decremented before preadbuffer(),
9732 * must inc next_to_pgetc: */
9733 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009734 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009735 }
Eric Andersencb57d552001-06-28 07:25:16 +00009736
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009737 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009738 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009739 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009740 again:
9741 more = preadfd();
9742 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009743 /* don't try reading again */
9744 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009745 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009747 return PEOF;
9748 }
9749 }
9750
Denis Vlasenko727752d2008-11-28 03:41:47 +00009751 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009752 * Set g_parsefile->left_in_line
9753 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009754 * NUL chars are deleted.
9755 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009756 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009759
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009760 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009761
Denis Vlasenko727752d2008-11-28 03:41:47 +00009762 c = *q;
9763 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009764 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009765 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009766 q++;
9767 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009768 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009769 break;
9770 }
Eric Andersencb57d552001-06-28 07:25:16 +00009771 }
9772
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009773 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009774 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9775 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009776 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009777 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009778 }
9779 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009780 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009781
Eric Andersencb57d552001-06-28 07:25:16 +00009782 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009783 char save = *q;
9784 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009785 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009786 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009787 }
9788
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009789 pgetc_debug("preadbuffer at %d:%p'%s'",
9790 g_parsefile->left_in_line,
9791 g_parsefile->next_to_pgetc,
9792 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009793 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009794}
9795
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009796#define pgetc_as_macro() \
9797 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009798 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009799 : preadbuffer() \
9800 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009801
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009802static int
9803pgetc(void)
9804{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009805 pgetc_debug("pgetc_fast at %d:%p'%s'",
9806 g_parsefile->left_in_line,
9807 g_parsefile->next_to_pgetc,
9808 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009809 return pgetc_as_macro();
9810}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009811
9812#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009813# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009814#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009815# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009816#endif
9817
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009818#if ENABLE_ASH_ALIAS
9819static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009820pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009821{
9822 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009823 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009824 pgetc_debug("pgetc_fast at %d:%p'%s'",
9825 g_parsefile->left_in_line,
9826 g_parsefile->next_to_pgetc,
9827 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009828 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009829 } while (c == PEOA);
9830 return c;
9831}
9832#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009833# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009834#endif
9835
9836/*
9837 * Read a line from the script.
9838 */
9839static char *
9840pfgets(char *line, int len)
9841{
9842 char *p = line;
9843 int nleft = len;
9844 int c;
9845
9846 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009847 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009848 if (c == PEOF) {
9849 if (p == line)
9850 return NULL;
9851 break;
9852 }
9853 *p++ = c;
9854 if (c == '\n')
9855 break;
9856 }
9857 *p = '\0';
9858 return line;
9859}
9860
Eric Andersenc470f442003-07-28 09:56:35 +00009861/*
9862 * Undo the last call to pgetc. Only one character may be pushed back.
9863 * PEOF may be pushed back.
9864 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009865static void
Eric Andersenc470f442003-07-28 09:56:35 +00009866pungetc(void)
9867{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009868 g_parsefile->left_in_line++;
9869 g_parsefile->next_to_pgetc--;
9870 pgetc_debug("pushed back to %d:%p'%s'",
9871 g_parsefile->left_in_line,
9872 g_parsefile->next_to_pgetc,
9873 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009874}
9875
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009876/*
9877 * To handle the "." command, a stack of input files is used. Pushfile
9878 * adds a new entry to the stack and popfile restores the previous level.
9879 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009880static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009881pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009882{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009883 struct parsefile *pf;
9884
Denis Vlasenko597906c2008-02-20 16:38:54 +00009885 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009886 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009887 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009888 /*pf->strpush = NULL; - ckzalloc did it */
9889 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009890 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009891}
9892
9893static void
9894popfile(void)
9895{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009896 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009897
Denis Vlasenkob012b102007-02-19 22:43:01 +00009898 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009899 if (pf->pf_fd >= 0)
9900 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009901 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009902 while (pf->strpush)
9903 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009904 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009905 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009906 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009907}
9908
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009909/*
9910 * Return to top level.
9911 */
9912static void
9913popallfiles(void)
9914{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009915 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009916 popfile();
9917}
9918
9919/*
9920 * Close the file(s) that the shell is reading commands from. Called
9921 * after a fork is done.
9922 */
9923static void
9924closescript(void)
9925{
9926 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009927 if (g_parsefile->pf_fd > 0) {
9928 close(g_parsefile->pf_fd);
9929 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009930 }
9931}
9932
9933/*
9934 * Like setinputfile, but takes an open file descriptor. Call this with
9935 * interrupts off.
9936 */
9937static void
9938setinputfd(int fd, int push)
9939{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009940 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009941 if (push) {
9942 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009943 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009944 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009945 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009946 if (g_parsefile->buf == NULL)
9947 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009948 g_parsefile->left_in_buffer = 0;
9949 g_parsefile->left_in_line = 0;
9950 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009951}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009952
Eric Andersenc470f442003-07-28 09:56:35 +00009953/*
9954 * Set the input to take input from a file. If push is set, push the
9955 * old input onto the stack first.
9956 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009957static int
9958setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009959{
9960 int fd;
9961 int fd2;
9962
Denis Vlasenkob012b102007-02-19 22:43:01 +00009963 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009964 fd = open(fname, O_RDONLY);
9965 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009966 if (flags & INPUT_NOFILE_OK)
9967 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009968 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009969 }
Eric Andersenc470f442003-07-28 09:56:35 +00009970 if (fd < 10) {
9971 fd2 = copyfd(fd, 10);
9972 close(fd);
9973 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009974 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009975 fd = fd2;
9976 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009977 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009978 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009979 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009980 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009981}
9982
Eric Andersencb57d552001-06-28 07:25:16 +00009983/*
9984 * Like setinputfile, but takes input from a string.
9985 */
Eric Andersenc470f442003-07-28 09:56:35 +00009986static void
9987setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009988{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009989 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009990 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009991 g_parsefile->next_to_pgetc = string;
9992 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009993 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009994 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009995 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009996}
9997
9998
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009999/* ============ mail.c
10000 *
10001 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010002 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010003
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010004#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010005
Eric Andersencb57d552001-06-28 07:25:16 +000010006#define MAXMBOXES 10
10007
Eric Andersenc470f442003-07-28 09:56:35 +000010008/* times of mailboxes */
10009static time_t mailtime[MAXMBOXES];
10010/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010011static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010012
Eric Andersencb57d552001-06-28 07:25:16 +000010013/*
Eric Andersenc470f442003-07-28 09:56:35 +000010014 * Print appropriate message(s) if mail has arrived.
10015 * If mail_var_path_changed is set,
10016 * then the value of MAIL has mail_var_path_changed,
10017 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010018 */
Eric Andersenc470f442003-07-28 09:56:35 +000010019static void
10020chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010021{
Eric Andersencb57d552001-06-28 07:25:16 +000010022 const char *mpath;
10023 char *p;
10024 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010025 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010026 struct stackmark smark;
10027 struct stat statb;
10028
Eric Andersencb57d552001-06-28 07:25:16 +000010029 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010030 mpath = mpathset() ? mpathval() : mailval();
10031 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010032 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010033 if (p == NULL)
10034 break;
10035 if (*p == '\0')
10036 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010037 for (q = p; *q; q++)
10038 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010039#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010040 if (q[-1] != '/')
10041 abort();
10042#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010043 q[-1] = '\0'; /* delete trailing '/' */
10044 if (stat(p, &statb) < 0) {
10045 *mtp = 0;
10046 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010047 }
Eric Andersenc470f442003-07-28 09:56:35 +000010048 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10049 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010050 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010051 pathopt ? pathopt : "you have mail"
10052 );
10053 }
10054 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010055 }
Eric Andersenc470f442003-07-28 09:56:35 +000010056 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010057 popstackmark(&smark);
10058}
Eric Andersencb57d552001-06-28 07:25:16 +000010059
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010060static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010061changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010062{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010063 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010064}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010065
Denis Vlasenko131ae172007-02-18 13:00:19 +000010066#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010067
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010068
10069/* ============ ??? */
10070
Eric Andersencb57d552001-06-28 07:25:16 +000010071/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010072 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010073 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010074static void
10075setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010076{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010077 char **newparam;
10078 char **ap;
10079 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010080
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010081 for (nparam = 0; argv[nparam]; nparam++)
10082 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010083 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10084 while (*argv) {
10085 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010086 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010087 *ap = NULL;
10088 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010089 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010090 shellparam.nparam = nparam;
10091 shellparam.p = newparam;
10092#if ENABLE_ASH_GETOPTS
10093 shellparam.optind = 1;
10094 shellparam.optoff = -1;
10095#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010096}
10097
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010098/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010099 * Process shell options. The global variable argptr contains a pointer
10100 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010101 *
10102 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10103 * For a non-interactive shell, an error condition encountered
10104 * by a special built-in ... shall cause the shell to write a diagnostic message
10105 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010106 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010107 * ...
10108 * Utility syntax error (option or operand error) Shall exit
10109 * ...
10110 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10111 * we see that bash does not do that (set "finishes" with error code 1 instead,
10112 * and shell continues), and people rely on this behavior!
10113 * Testcase:
10114 * set -o barfoo 2>/dev/null
10115 * echo $?
10116 *
10117 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010118 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010119static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010120plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010121{
10122 int i;
10123
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010124 if (name) {
10125 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010126 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010127 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010128 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010129 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010130 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010131 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010132 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010133 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010134 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010135 if (val) {
10136 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10137 } else {
10138 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10139 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010140 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010141 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010142}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010143static void
10144setoption(int flag, int val)
10145{
10146 int i;
10147
10148 for (i = 0; i < NOPTS; i++) {
10149 if (optletters(i) == flag) {
10150 optlist[i] = val;
10151 return;
10152 }
10153 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010154 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010155 /* NOTREACHED */
10156}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010157static int
Eric Andersenc470f442003-07-28 09:56:35 +000010158options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010159{
10160 char *p;
10161 int val;
10162 int c;
10163
10164 if (cmdline)
10165 minusc = NULL;
10166 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010167 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010168 if (c != '-' && c != '+')
10169 break;
10170 argptr++;
10171 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010172 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010173 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010174 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010175 if (!cmdline) {
10176 /* "-" means turn off -x and -v */
10177 if (p[0] == '\0')
10178 xflag = vflag = 0;
10179 /* "--" means reset params */
10180 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010181 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010182 }
Eric Andersenc470f442003-07-28 09:56:35 +000010183 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010184 }
Eric Andersencb57d552001-06-28 07:25:16 +000010185 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010186 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010187 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010188 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010189 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010190 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010191 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010192 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010193 /* it already printed err message */
10194 return 1; /* error */
10195 }
Eric Andersencb57d552001-06-28 07:25:16 +000010196 if (*argptr)
10197 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010198 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10199 isloginsh = 1;
10200 /* bash does not accept +-login, we also won't */
10201 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010202 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010203 isloginsh = 1;
10204 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010205 } else {
10206 setoption(c, val);
10207 }
10208 }
10209 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010210 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010211}
10212
Eric Andersencb57d552001-06-28 07:25:16 +000010213/*
Eric Andersencb57d552001-06-28 07:25:16 +000010214 * The shift builtin command.
10215 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010216static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010217shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010218{
10219 int n;
10220 char **ap1, **ap2;
10221
10222 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010223 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010224 n = number(argv[1]);
10225 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010226 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010227 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010228 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010229 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010230 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010231 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010232 }
10233 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010234 while ((*ap2++ = *ap1++) != NULL)
10235 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010236#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010237 shellparam.optind = 1;
10238 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010239#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010240 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010241 return 0;
10242}
10243
Eric Andersencb57d552001-06-28 07:25:16 +000010244/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010245 * POSIX requires that 'set' (but not export or readonly) output the
10246 * variables in lexicographic order - by the locale's collating order (sigh).
10247 * Maybe we could keep them in an ordered balanced binary tree
10248 * instead of hashed lists.
10249 * For now just roll 'em through qsort for printing...
10250 */
10251static int
10252showvars(const char *sep_prefix, int on, int off)
10253{
10254 const char *sep;
10255 char **ep, **epend;
10256
10257 ep = listvars(on, off, &epend);
10258 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10259
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010260 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010261
10262 for (; ep < epend; ep++) {
10263 const char *p;
10264 const char *q;
10265
10266 p = strchrnul(*ep, '=');
10267 q = nullstr;
10268 if (*p)
10269 q = single_quote(++p);
10270 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10271 }
10272 return 0;
10273}
10274
10275/*
Eric Andersencb57d552001-06-28 07:25:16 +000010276 * The set command builtin.
10277 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010278static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010279setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010280{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010281 int retval;
10282
Denis Vlasenko68404f12008-03-17 09:00:54 +000010283 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010284 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010285 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010286 retval = 1;
10287 if (!options(0)) { /* if no parse error... */
10288 retval = 0;
10289 optschanged();
10290 if (*argptr != NULL) {
10291 setparam(argptr);
10292 }
Eric Andersencb57d552001-06-28 07:25:16 +000010293 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010294 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010295 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010296}
10297
Denis Vlasenko131ae172007-02-18 13:00:19 +000010298#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010299static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010300change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010301{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010302 uint32_t t;
10303
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010304 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010305 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010306 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010307 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010308 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010309 vrandom.flags &= ~VNOFUNC;
10310 } else {
10311 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010312 t = strtoul(value, NULL, 10);
10313 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010314 }
Eric Andersenef02f822004-03-11 13:34:24 +000010315}
Eric Andersen16767e22004-03-16 05:14:10 +000010316#endif
10317
Denis Vlasenko131ae172007-02-18 13:00:19 +000010318#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010319static int
Eric Andersenc470f442003-07-28 09:56:35 +000010320getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010321{
10322 char *p, *q;
10323 char c = '?';
10324 int done = 0;
10325 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010326 char s[12];
10327 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010328
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010329 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010330 return 1;
10331 optnext = optfirst + *param_optind - 1;
10332
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010333 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010334 p = NULL;
10335 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010336 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010337 if (p == NULL || *p == '\0') {
10338 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010339 p = *optnext;
10340 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010341 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010342 p = NULL;
10343 done = 1;
10344 goto out;
10345 }
10346 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010347 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010348 goto atend;
10349 }
10350
10351 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010352 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010353 if (*q == '\0') {
10354 if (optstr[0] == ':') {
10355 s[0] = c;
10356 s[1] = '\0';
10357 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010358 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010359 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010360 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010361 }
10362 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010363 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010364 }
10365 if (*++q == ':')
10366 q++;
10367 }
10368
10369 if (*++q == ':') {
10370 if (*p == '\0' && (p = *optnext) == NULL) {
10371 if (optstr[0] == ':') {
10372 s[0] = c;
10373 s[1] = '\0';
10374 err |= setvarsafe("OPTARG", s, 0);
10375 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010376 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010377 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010378 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010379 c = '?';
10380 }
Eric Andersenc470f442003-07-28 09:56:35 +000010381 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010382 }
10383
10384 if (p == *optnext)
10385 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010386 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010387 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010388 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010389 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010390 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010391 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010392 *param_optind = optnext - optfirst + 1;
10393 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010394 err |= setvarsafe("OPTIND", s, VNOFUNC);
10395 s[0] = c;
10396 s[1] = '\0';
10397 err |= setvarsafe(optvar, s, 0);
10398 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010399 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010400 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010401 flush_stdout_stderr();
10402 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010403 }
10404 return done;
10405}
Eric Andersenc470f442003-07-28 09:56:35 +000010406
10407/*
10408 * The getopts builtin. Shellparam.optnext points to the next argument
10409 * to be processed. Shellparam.optptr points to the next character to
10410 * be processed in the current argument. If shellparam.optnext is NULL,
10411 * then it's the first time getopts has been called.
10412 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010413static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010414getoptscmd(int argc, char **argv)
10415{
10416 char **optbase;
10417
10418 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010419 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010420 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010421 optbase = shellparam.p;
10422 if (shellparam.optind > shellparam.nparam + 1) {
10423 shellparam.optind = 1;
10424 shellparam.optoff = -1;
10425 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010426 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010427 optbase = &argv[3];
10428 if (shellparam.optind > argc - 2) {
10429 shellparam.optind = 1;
10430 shellparam.optoff = -1;
10431 }
10432 }
10433
10434 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010435 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010436}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010437#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010438
Eric Andersencb57d552001-06-28 07:25:16 +000010439
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010440/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010441
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010442struct heredoc {
10443 struct heredoc *next; /* next here document in list */
10444 union node *here; /* redirection node */
10445 char *eofmark; /* string indicating end of input */
10446 smallint striptabs; /* if set, strip leading tabs */
10447};
10448
10449static smallint tokpushback; /* last token pushed back */
10450static smallint parsebackquote; /* nonzero if we are inside backquotes */
10451static smallint quoteflag; /* set if (part of) last token was quoted */
10452static token_id_t lasttoken; /* last token read (integer id Txxx) */
10453static struct heredoc *heredoclist; /* list of here documents to read */
10454static char *wordtext; /* text of last word returned by readtoken */
10455static struct nodelist *backquotelist;
10456static union node *redirnode;
10457static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010458
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010459static const char *
10460tokname(char *buf, int tok)
10461{
10462 if (tok < TSEMI)
10463 return tokname_array[tok] + 1;
10464 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10465 return buf;
10466}
10467
10468/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010469 * Called when an unexpected token is read during the parse. The argument
10470 * is the token that is expected, or -1 if more than one type of token can
10471 * occur at this point.
10472 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010473static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010474static void
10475raise_error_unexpected_syntax(int token)
10476{
10477 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010478 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010479 int l;
10480
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010481 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010482 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010483 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010484 raise_error_syntax(msg);
10485 /* NOTREACHED */
10486}
Eric Andersencb57d552001-06-28 07:25:16 +000010487
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010488#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010489
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010490/* parsing is heavily cross-recursive, need these forward decls */
10491static union node *andor(void);
10492static union node *pipeline(void);
10493static union node *parse_command(void);
10494static void parseheredoc(void);
10495static char peektoken(void);
10496static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010497
Eric Andersenc470f442003-07-28 09:56:35 +000010498static union node *
10499list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010500{
10501 union node *n1, *n2, *n3;
10502 int tok;
10503
Eric Andersenc470f442003-07-28 09:56:35 +000010504 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10505 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010506 return NULL;
10507 n1 = NULL;
10508 for (;;) {
10509 n2 = andor();
10510 tok = readtoken();
10511 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010512 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010513 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010514 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010515 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010516 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010517 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010518 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010519 n2 = n3;
10520 }
10521 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010522 }
10523 }
10524 if (n1 == NULL) {
10525 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010526 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010527 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010528 n3->type = NSEMI;
10529 n3->nbinary.ch1 = n1;
10530 n3->nbinary.ch2 = n2;
10531 n1 = n3;
10532 }
10533 switch (tok) {
10534 case TBACKGND:
10535 case TSEMI:
10536 tok = readtoken();
10537 /* fall through */
10538 case TNL:
10539 if (tok == TNL) {
10540 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010541 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010542 return n1;
10543 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010544 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010545 }
Eric Andersenc470f442003-07-28 09:56:35 +000010546 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010547 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010548 return n1;
10549 break;
10550 case TEOF:
10551 if (heredoclist)
10552 parseheredoc();
10553 else
Eric Andersenc470f442003-07-28 09:56:35 +000010554 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010555 return n1;
10556 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010557 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010558 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010559 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010560 return n1;
10561 }
10562 }
10563}
10564
Eric Andersenc470f442003-07-28 09:56:35 +000010565static union node *
10566andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010567{
Eric Andersencb57d552001-06-28 07:25:16 +000010568 union node *n1, *n2, *n3;
10569 int t;
10570
Eric Andersencb57d552001-06-28 07:25:16 +000010571 n1 = pipeline();
10572 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010573 t = readtoken();
10574 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010575 t = NAND;
10576 } else if (t == TOR) {
10577 t = NOR;
10578 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010579 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010580 return n1;
10581 }
Eric Andersenc470f442003-07-28 09:56:35 +000010582 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010583 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010584 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010585 n3->type = t;
10586 n3->nbinary.ch1 = n1;
10587 n3->nbinary.ch2 = n2;
10588 n1 = n3;
10589 }
10590}
10591
Eric Andersenc470f442003-07-28 09:56:35 +000010592static union node *
10593pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010594{
Eric Andersencb57d552001-06-28 07:25:16 +000010595 union node *n1, *n2, *pipenode;
10596 struct nodelist *lp, *prev;
10597 int negate;
10598
10599 negate = 0;
10600 TRACE(("pipeline: entered\n"));
10601 if (readtoken() == TNOT) {
10602 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010603 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010604 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010605 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010606 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010607 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010608 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010609 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010610 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010611 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010612 pipenode->npipe.cmdlist = lp;
10613 lp->n = n1;
10614 do {
10615 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010616 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010617 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010618 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010619 prev->next = lp;
10620 } while (readtoken() == TPIPE);
10621 lp->next = NULL;
10622 n1 = pipenode;
10623 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010624 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010625 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010626 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010627 n2->type = NNOT;
10628 n2->nnot.com = n1;
10629 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010630 }
10631 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010632}
10633
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010634static union node *
10635makename(void)
10636{
10637 union node *n;
10638
Denis Vlasenko597906c2008-02-20 16:38:54 +000010639 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010641 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010642 n->narg.text = wordtext;
10643 n->narg.backquote = backquotelist;
10644 return n;
10645}
10646
10647static void
10648fixredir(union node *n, const char *text, int err)
10649{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010650 int fd;
10651
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010652 TRACE(("Fix redir %s %d\n", text, err));
10653 if (!err)
10654 n->ndup.vname = NULL;
10655
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010656 fd = bb_strtou(text, NULL, 10);
10657 if (!errno && fd >= 0)
10658 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010659 else if (LONE_DASH(text))
10660 n->ndup.dupfd = -1;
10661 else {
10662 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010663 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010664 n->ndup.vname = makename();
10665 }
10666}
10667
10668/*
10669 * Returns true if the text contains nothing to expand (no dollar signs
10670 * or backquotes).
10671 */
10672static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010673noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010674{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010675 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010676
Denys Vlasenkocd716832009-11-28 22:14:02 +010010677 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010678 if (c == CTLQUOTEMARK)
10679 continue;
10680 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010681 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010682 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010683 return 0;
10684 }
10685 return 1;
10686}
10687
10688static void
10689parsefname(void)
10690{
10691 union node *n = redirnode;
10692
10693 if (readtoken() != TWORD)
10694 raise_error_unexpected_syntax(-1);
10695 if (n->type == NHERE) {
10696 struct heredoc *here = heredoc;
10697 struct heredoc *p;
10698 int i;
10699
10700 if (quoteflag == 0)
10701 n->type = NXHERE;
10702 TRACE(("Here document %d\n", n->type));
10703 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010704 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010705 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010706 here->eofmark = wordtext;
10707 here->next = NULL;
10708 if (heredoclist == NULL)
10709 heredoclist = here;
10710 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010711 for (p = heredoclist; p->next; p = p->next)
10712 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010713 p->next = here;
10714 }
10715 } else if (n->type == NTOFD || n->type == NFROMFD) {
10716 fixredir(n, wordtext, 0);
10717 } else {
10718 n->nfile.fname = makename();
10719 }
10720}
Eric Andersencb57d552001-06-28 07:25:16 +000010721
Eric Andersenc470f442003-07-28 09:56:35 +000010722static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010723simplecmd(void)
10724{
10725 union node *args, **app;
10726 union node *n = NULL;
10727 union node *vars, **vpp;
10728 union node **rpp, *redir;
10729 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010730#if ENABLE_ASH_BASH_COMPAT
10731 smallint double_brackets_flag = 0;
10732#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733
10734 args = NULL;
10735 app = &args;
10736 vars = NULL;
10737 vpp = &vars;
10738 redir = NULL;
10739 rpp = &redir;
10740
10741 savecheckkwd = CHKALIAS;
10742 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010743 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010744 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010745 t = readtoken();
10746 switch (t) {
10747#if ENABLE_ASH_BASH_COMPAT
10748 case TAND: /* "&&" */
10749 case TOR: /* "||" */
10750 if (!double_brackets_flag) {
10751 tokpushback = 1;
10752 goto out;
10753 }
10754 wordtext = (char *) (t == TAND ? "-a" : "-o");
10755#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010756 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010757 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010758 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010759 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010760 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010761#if ENABLE_ASH_BASH_COMPAT
10762 if (strcmp("[[", wordtext) == 0)
10763 double_brackets_flag = 1;
10764 else if (strcmp("]]", wordtext) == 0)
10765 double_brackets_flag = 0;
10766#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010767 n->narg.backquote = backquotelist;
10768 if (savecheckkwd && isassignment(wordtext)) {
10769 *vpp = n;
10770 vpp = &n->narg.next;
10771 } else {
10772 *app = n;
10773 app = &n->narg.next;
10774 savecheckkwd = 0;
10775 }
10776 break;
10777 case TREDIR:
10778 *rpp = n = redirnode;
10779 rpp = &n->nfile.next;
10780 parsefname(); /* read name of redirection file */
10781 break;
10782 case TLP:
10783 if (args && app == &args->narg.next
10784 && !vars && !redir
10785 ) {
10786 struct builtincmd *bcmd;
10787 const char *name;
10788
10789 /* We have a function */
10790 if (readtoken() != TRP)
10791 raise_error_unexpected_syntax(TRP);
10792 name = n->narg.text;
10793 if (!goodname(name)
10794 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10795 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010796 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010797 }
10798 n->type = NDEFUN;
10799 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10800 n->narg.next = parse_command();
10801 return n;
10802 }
10803 /* fall through */
10804 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010805 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010806 goto out;
10807 }
10808 }
10809 out:
10810 *app = NULL;
10811 *vpp = NULL;
10812 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010813 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010814 n->type = NCMD;
10815 n->ncmd.args = args;
10816 n->ncmd.assign = vars;
10817 n->ncmd.redirect = redir;
10818 return n;
10819}
10820
10821static union node *
10822parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010823{
Eric Andersencb57d552001-06-28 07:25:16 +000010824 union node *n1, *n2;
10825 union node *ap, **app;
10826 union node *cp, **cpp;
10827 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010828 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010829 int t;
10830
10831 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010832 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010833
Eric Andersencb57d552001-06-28 07:25:16 +000010834 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010835 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010836 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010837 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010838 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010839 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010840 n1->type = NIF;
10841 n1->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 n1->nif.ifpart = list(0);
10845 n2 = n1;
10846 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010847 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010848 n2 = n2->nif.elsepart;
10849 n2->type = NIF;
10850 n2->nif.test = list(0);
10851 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010852 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010853 n2->nif.ifpart = list(0);
10854 }
10855 if (lasttoken == TELSE)
10856 n2->nif.elsepart = list(0);
10857 else {
10858 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010859 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010860 }
Eric Andersenc470f442003-07-28 09:56:35 +000010861 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010862 break;
10863 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010864 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010865 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010866 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010867 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010868 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010869 got = readtoken();
10870 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010871 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010872 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010873 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010874 }
10875 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010876 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010877 break;
10878 }
10879 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010880 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010881 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010882 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010883 n1->type = NFOR;
10884 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010885 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010886 if (readtoken() == TIN) {
10887 app = &ap;
10888 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010889 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010890 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010891 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010892 n2->narg.text = wordtext;
10893 n2->narg.backquote = backquotelist;
10894 *app = n2;
10895 app = &n2->narg.next;
10896 }
10897 *app = NULL;
10898 n1->nfor.args = ap;
10899 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010900 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010901 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010902 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010903 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010904 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010905 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010906 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010907 n1->nfor.args = n2;
10908 /*
10909 * Newline or semicolon here is optional (but note
10910 * that the original Bourne shell only allowed NL).
10911 */
10912 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010913 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010914 }
Eric Andersenc470f442003-07-28 09:56:35 +000010915 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010916 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010917 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010918 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010919 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010920 break;
10921 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010922 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010923 n1->type = NCASE;
10924 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010925 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010926 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010927 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010928 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010929 n2->narg.text = wordtext;
10930 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010932 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010933 } while (readtoken() == TNL);
10934 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010935 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010936 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010937 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010938 checkkwd = CHKNL | CHKKWD;
10939 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010940 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010941 if (lasttoken == TLP)
10942 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010943 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010944 cp->type = NCLIST;
10945 app = &cp->nclist.pattern;
10946 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010947 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010948 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010949 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010950 ap->narg.text = wordtext;
10951 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010952 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010953 break;
10954 app = &ap->narg.next;
10955 readtoken();
10956 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010957 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010958 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010959 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010960 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010961
Eric Andersenc470f442003-07-28 09:56:35 +000010962 cpp = &cp->nclist.next;
10963
10964 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010965 t = readtoken();
10966 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010967 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010968 raise_error_unexpected_syntax(TENDCASE);
10969 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010970 }
Eric Andersenc470f442003-07-28 09:56:35 +000010971 }
Eric Andersencb57d552001-06-28 07:25:16 +000010972 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010973 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010974 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010975 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010976 n1->type = NSUBSHELL;
10977 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010978 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010979 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010980 break;
10981 case TBEGIN:
10982 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010983 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010984 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010985 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010986 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010987 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010988 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010989 }
10990
Eric Andersenc470f442003-07-28 09:56:35 +000010991 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010992 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010993
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010994 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010995 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010996 checkkwd = CHKKWD | CHKALIAS;
10997 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010998 while (readtoken() == TREDIR) {
10999 *rpp = n2 = redirnode;
11000 rpp = &n2->nfile.next;
11001 parsefname();
11002 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011003 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011004 *rpp = NULL;
11005 if (redir) {
11006 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011007 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011008 n2->type = NREDIR;
11009 n2->nredir.n = n1;
11010 n1 = n2;
11011 }
11012 n1->nredir.redirect = redir;
11013 }
Eric Andersencb57d552001-06-28 07:25:16 +000011014 return n1;
11015}
11016
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011017#if ENABLE_ASH_BASH_COMPAT
11018static int decode_dollar_squote(void)
11019{
11020 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11021 int c, cnt;
11022 char *p;
11023 char buf[4];
11024
11025 c = pgetc();
11026 p = strchr(C_escapes, c);
11027 if (p) {
11028 buf[0] = c;
11029 p = buf;
11030 cnt = 3;
11031 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11032 do {
11033 c = pgetc();
11034 *++p = c;
11035 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11036 pungetc();
11037 } else if (c == 'x') { /* \xHH */
11038 do {
11039 c = pgetc();
11040 *++p = c;
11041 } while (isxdigit(c) && --cnt);
11042 pungetc();
11043 if (cnt == 3) { /* \x but next char is "bad" */
11044 c = 'x';
11045 goto unrecognized;
11046 }
11047 } else { /* simple seq like \\ or \t */
11048 p++;
11049 }
11050 *p = '\0';
11051 p = buf;
11052 c = bb_process_escape_sequence((void*)&p);
11053 } else { /* unrecognized "\z": print both chars unless ' or " */
11054 if (c != '\'' && c != '"') {
11055 unrecognized:
11056 c |= 0x100; /* "please encode \, then me" */
11057 }
11058 }
11059 return c;
11060}
11061#endif
11062
Eric Andersencb57d552001-06-28 07:25:16 +000011063/*
11064 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11065 * is not NULL, read a here document. In the latter case, eofmark is the
11066 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011067 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011068 * is the first character of the input token or document.
11069 *
11070 * Because C does not have internal subroutines, I have simulated them
11071 * using goto's to implement the subroutine linkage. The following macros
11072 * will run code that appears at the end of readtoken1.
11073 */
Eric Andersen2870d962001-07-02 17:27:21 +000011074#define CHECKEND() {goto checkend; checkend_return:;}
11075#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11076#define PARSESUB() {goto parsesub; parsesub_return:;}
11077#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11078#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11079#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011080static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011081readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011082{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011083 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011084 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011085 char *out;
11086 int len;
11087 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011088 struct nodelist *bqlist;
11089 smallint quotef;
11090 smallint dblquote;
11091 smallint oldstyle;
11092 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011093#if ENABLE_ASH_EXPAND_PRMT
11094 smallint pssyntax; /* we are expanding a prompt string */
11095#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011096 int varnest; /* levels of variables expansion */
11097 int arinest; /* levels of arithmetic expansion */
11098 int parenlevel; /* levels of parens in arithmetic */
11099 int dqvarnest; /* levels of variables expansion within double quotes */
11100
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011101 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011102
Eric Andersencb57d552001-06-28 07:25:16 +000011103#if __GNUC__
11104 /* Avoid longjmp clobbering */
11105 (void) &out;
11106 (void) &quotef;
11107 (void) &dblquote;
11108 (void) &varnest;
11109 (void) &arinest;
11110 (void) &parenlevel;
11111 (void) &dqvarnest;
11112 (void) &oldstyle;
11113 (void) &prevsyntax;
11114 (void) &syntax;
11115#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011116 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011117 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011118 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011119 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011120#if ENABLE_ASH_EXPAND_PRMT
11121 pssyntax = (syntax == PSSYNTAX);
11122 if (pssyntax)
11123 syntax = DQSYNTAX;
11124#endif
11125 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011126 varnest = 0;
11127 arinest = 0;
11128 parenlevel = 0;
11129 dqvarnest = 0;
11130
11131 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011132 loop:
11133 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011134 CHECKEND(); /* set c to PEOF if at end of here document */
11135 for (;;) { /* until end of line or end of word */
11136 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11137 switch (SIT(c, syntax)) {
11138 case CNL: /* '\n' */
11139 if (syntax == BASESYNTAX)
11140 goto endword; /* exit outer loop */
11141 USTPUTC(c, out);
11142 g_parsefile->linno++;
11143 setprompt_if(doprompt, 2);
11144 c = pgetc();
11145 goto loop; /* continue outer loop */
11146 case CWORD:
11147 USTPUTC(c, out);
11148 break;
11149 case CCTL:
11150 if (eofmark == NULL || dblquote)
11151 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011152#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011153 if (c == '\\' && bash_dollar_squote) {
11154 c = decode_dollar_squote();
11155 if (c & 0x100) {
11156 USTPUTC('\\', out);
11157 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011158 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011159 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011160#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011161 USTPUTC(c, out);
11162 break;
11163 case CBACK: /* backslash */
11164 c = pgetc_without_PEOA();
11165 if (c == PEOF) {
11166 USTPUTC(CTLESC, out);
11167 USTPUTC('\\', out);
11168 pungetc();
11169 } else if (c == '\n') {
11170 setprompt_if(doprompt, 2);
11171 } else {
11172#if ENABLE_ASH_EXPAND_PRMT
11173 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011174 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011175 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011176 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011177#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011178 /* Backslash is retained if we are in "str" and next char isn't special */
11179 if (dblquote
11180 && c != '\\'
11181 && c != '`'
11182 && c != '$'
11183 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011184 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011185 USTPUTC(CTLESC, out);
11186 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011187 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011188 if (SIT(c, SQSYNTAX) == CCTL)
11189 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011190 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011191 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011192 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011193 break;
11194 case CSQUOTE:
11195 syntax = SQSYNTAX;
11196 quotemark:
11197 if (eofmark == NULL) {
11198 USTPUTC(CTLQUOTEMARK, out);
11199 }
11200 break;
11201 case CDQUOTE:
11202 syntax = DQSYNTAX;
11203 dblquote = 1;
11204 goto quotemark;
11205 case CENDQUOTE:
11206 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11207 if (eofmark != NULL && arinest == 0
11208 && varnest == 0
11209 ) {
11210 USTPUTC(c, out);
11211 } else {
11212 if (dqvarnest == 0) {
11213 syntax = BASESYNTAX;
11214 dblquote = 0;
11215 }
11216 quotef = 1;
11217 goto quotemark;
11218 }
11219 break;
11220 case CVAR: /* '$' */
11221 PARSESUB(); /* parse substitution */
11222 break;
11223 case CENDVAR: /* '}' */
11224 if (varnest > 0) {
11225 varnest--;
11226 if (dqvarnest > 0) {
11227 dqvarnest--;
11228 }
11229 c = CTLENDVAR;
11230 }
11231 USTPUTC(c, out);
11232 break;
11233#if ENABLE_SH_MATH_SUPPORT
11234 case CLP: /* '(' in arithmetic */
11235 parenlevel++;
11236 USTPUTC(c, out);
11237 break;
11238 case CRP: /* ')' in arithmetic */
11239 if (parenlevel > 0) {
11240 parenlevel--;
11241 } else {
11242 if (pgetc() == ')') {
11243 if (--arinest == 0) {
11244 syntax = prevsyntax;
11245 dblquote = (syntax == DQSYNTAX);
11246 c = CTLENDARI;
11247 }
11248 } else {
11249 /*
11250 * unbalanced parens
11251 * (don't 2nd guess - no error)
11252 */
11253 pungetc();
11254 }
11255 }
11256 USTPUTC(c, out);
11257 break;
11258#endif
11259 case CBQUOTE: /* '`' */
11260 PARSEBACKQOLD();
11261 break;
11262 case CENDFILE:
11263 goto endword; /* exit outer loop */
11264 case CIGN:
11265 break;
11266 default:
11267 if (varnest == 0) {
11268#if ENABLE_ASH_BASH_COMPAT
11269 if (c == '&') {
11270 if (pgetc() == '>')
11271 c = 0x100 + '>'; /* flag &> */
11272 pungetc();
11273 }
11274#endif
11275 goto endword; /* exit outer loop */
11276 }
11277 IF_ASH_ALIAS(if (c != PEOA))
11278 USTPUTC(c, out);
11279 }
11280 c = pgetc_fast();
11281 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011282 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011283
Mike Frysinger98c52642009-04-02 10:02:37 +000011284#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011285 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011286 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011287#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011288 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011289 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011290 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011291 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011292 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011293 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011294 }
11295 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011296 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011297 out = stackblock();
11298 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011299 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011300 && quotef == 0
11301 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011302 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011303 PARSEREDIR(); /* passed as params: out, c */
11304 lasttoken = TREDIR;
11305 return lasttoken;
11306 }
11307 /* else: non-number X seen, interpret it
11308 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011309 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011310 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011311 }
11312 quoteflag = quotef;
11313 backquotelist = bqlist;
11314 grabstackblock(len);
11315 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011316 lasttoken = TWORD;
11317 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011318/* end of readtoken routine */
11319
Eric Andersencb57d552001-06-28 07:25:16 +000011320/*
11321 * Check to see whether we are at the end of the here document. When this
11322 * is called, c is set to the first character of the next input line. If
11323 * we are at the end of the here document, this routine sets the c to PEOF.
11324 */
Eric Andersenc470f442003-07-28 09:56:35 +000011325checkend: {
11326 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011327#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011328 if (c == PEOA)
11329 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011330#endif
11331 if (striptabs) {
11332 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011333 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011334 }
Eric Andersenc470f442003-07-28 09:56:35 +000011335 }
11336 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011337 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011338 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011339
Eric Andersenc470f442003-07-28 09:56:35 +000011340 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011341 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11342 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011343 if (*p == '\n' && *q == '\0') {
11344 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011345 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011346 needprompt = doprompt;
11347 } else {
11348 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011349 }
11350 }
11351 }
11352 }
Eric Andersenc470f442003-07-28 09:56:35 +000011353 goto checkend_return;
11354}
Eric Andersencb57d552001-06-28 07:25:16 +000011355
Eric Andersencb57d552001-06-28 07:25:16 +000011356/*
11357 * Parse a redirection operator. The variable "out" points to a string
11358 * specifying the fd to be redirected. The variable "c" contains the
11359 * first character of the redirection operator.
11360 */
Eric Andersenc470f442003-07-28 09:56:35 +000011361parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011362 /* out is already checked to be a valid number or "" */
11363 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011364 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011365
Denis Vlasenko597906c2008-02-20 16:38:54 +000011366 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011367 if (c == '>') {
11368 np->nfile.fd = 1;
11369 c = pgetc();
11370 if (c == '>')
11371 np->type = NAPPEND;
11372 else if (c == '|')
11373 np->type = NCLOBBER;
11374 else if (c == '&')
11375 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011376 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011377 else {
11378 np->type = NTO;
11379 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011380 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011381 }
11382#if ENABLE_ASH_BASH_COMPAT
11383 else if (c == 0x100 + '>') { /* this flags &> redirection */
11384 np->nfile.fd = 1;
11385 pgetc(); /* this is '>', no need to check */
11386 np->type = NTO2;
11387 }
11388#endif
11389 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011390 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011391 c = pgetc();
11392 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011393 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011394 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011395 np = stzalloc(sizeof(struct nhere));
11396 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 }
11398 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011399 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011400 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011401 c = pgetc();
11402 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011403 heredoc->striptabs = 1;
11404 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011405 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011406 pungetc();
11407 }
11408 break;
11409
11410 case '&':
11411 np->type = NFROMFD;
11412 break;
11413
11414 case '>':
11415 np->type = NFROMTO;
11416 break;
11417
11418 default:
11419 np->type = NFROM;
11420 pungetc();
11421 break;
11422 }
Eric Andersencb57d552001-06-28 07:25:16 +000011423 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011424 if (fd >= 0)
11425 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011426 redirnode = np;
11427 goto parseredir_return;
11428}
Eric Andersencb57d552001-06-28 07:25:16 +000011429
Eric Andersencb57d552001-06-28 07:25:16 +000011430/*
11431 * Parse a substitution. At this point, we have read the dollar sign
11432 * and nothing else.
11433 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011434
11435/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11436 * (assuming ascii char codes, as the original implementation did) */
11437#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011438 (((unsigned)(c) - 33 < 32) \
11439 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011440parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011441 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011442 int typeloc;
11443 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011444
Eric Andersenc470f442003-07-28 09:56:35 +000011445 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011446 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011447 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011448 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011449#if ENABLE_ASH_BASH_COMPAT
11450 if (c == '\'')
11451 bash_dollar_squote = 1;
11452 else
11453#endif
11454 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011455 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011456 } else if (c == '(') {
11457 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011458 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011459#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011460 PARSEARITH();
11461#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011462 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011463#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011464 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011465 pungetc();
11466 PARSEBACKQNEW();
11467 }
11468 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011469 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011470 USTPUTC(CTLVAR, out);
11471 typeloc = out - (char *)stackblock();
11472 USTPUTC(VSNORMAL, out);
11473 subtype = VSNORMAL;
11474 if (c == '{') {
11475 c = pgetc();
11476 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011477 c = pgetc();
11478 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011479 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011480 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011481 subtype = VSLENGTH; /* ${#VAR} */
11482 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011483 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011484 }
Eric Andersenc470f442003-07-28 09:56:35 +000011485 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011486 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011487 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011488 do {
11489 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011490 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011491 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011492 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011493 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011494 do {
11495 STPUTC(c, out);
11496 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011497 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011498 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011499 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011500 USTPUTC(c, out);
11501 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011502 } else {
11503 badsub:
11504 raise_error_syntax("bad substitution");
11505 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011506 if (c != '}' && subtype == VSLENGTH) {
11507 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011508 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011509 }
Eric Andersencb57d552001-06-28 07:25:16 +000011510
Eric Andersenc470f442003-07-28 09:56:35 +000011511 STPUTC('=', out);
11512 flags = 0;
11513 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011514 /* ${VAR...} but not $VAR or ${#VAR} */
11515 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011516 switch (c) {
11517 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011518 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011519#if ENABLE_ASH_BASH_COMPAT
11520 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011521//TODO: support more general format ${v:EXPR:EXPR},
11522// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011523 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011524 pungetc();
11525 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011526 }
11527#endif
11528 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011529 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011530 default: {
11531 static const char types[] ALIGN1 = "}-+?=";
11532 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011533 if (p == NULL)
11534 goto badsub;
11535 subtype = p - types + VSNORMAL;
11536 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011537 }
Eric Andersenc470f442003-07-28 09:56:35 +000011538 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011539 case '#': {
11540 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011541 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011542 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011543 if (c != cc)
11544 goto do_pungetc;
11545 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011546 break;
11547 }
11548#if ENABLE_ASH_BASH_COMPAT
11549 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011550 /* ${v/[/]pattern/repl} */
11551//TODO: encode pattern and repl separately.
11552// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011553 subtype = VSREPLACE;
11554 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011555 if (c != '/')
11556 goto do_pungetc;
11557 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011558 break;
11559#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011560 }
Eric Andersenc470f442003-07-28 09:56:35 +000011561 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011562 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011563 pungetc();
11564 }
11565 if (dblquote || arinest)
11566 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011567 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011568 if (subtype != VSNORMAL) {
11569 varnest++;
11570 if (dblquote || arinest) {
11571 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011572 }
11573 }
11574 }
Eric Andersenc470f442003-07-28 09:56:35 +000011575 goto parsesub_return;
11576}
Eric Andersencb57d552001-06-28 07:25:16 +000011577
Eric Andersencb57d552001-06-28 07:25:16 +000011578/*
11579 * Called to parse command substitutions. Newstyle is set if the command
11580 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11581 * list of commands (passed by reference), and savelen is the number of
11582 * characters on the top of the stack which must be preserved.
11583 */
Eric Andersenc470f442003-07-28 09:56:35 +000011584parsebackq: {
11585 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011586 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011587 union node *n;
11588 char *volatile str;
11589 struct jmploc jmploc;
11590 struct jmploc *volatile savehandler;
11591 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011592 smallint saveprompt = 0;
11593
Eric Andersencb57d552001-06-28 07:25:16 +000011594#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011595 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011596#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011597 savepbq = parsebackquote;
11598 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011599 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011600 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011601 exception_handler = savehandler;
11602 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011603 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011604 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011605 str = NULL;
11606 savelen = out - (char *)stackblock();
11607 if (savelen > 0) {
11608 str = ckmalloc(savelen);
11609 memcpy(str, stackblock(), savelen);
11610 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011611 savehandler = exception_handler;
11612 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011613 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011614 if (oldstyle) {
11615 /* We must read until the closing backquote, giving special
11616 treatment to some slashes, and then push the string and
11617 reread it as input, interpreting it normally. */
11618 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011619 size_t psavelen;
11620 char *pstr;
11621
Eric Andersenc470f442003-07-28 09:56:35 +000011622 STARTSTACKSTR(pout);
11623 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011624 int pc;
11625
11626 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011627 pc = pgetc();
11628 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011629 case '`':
11630 goto done;
11631
11632 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011633 pc = pgetc();
11634 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011635 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011636 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011637 /*
11638 * If eating a newline, avoid putting
11639 * the newline into the new character
11640 * stream (via the STPUTC after the
11641 * switch).
11642 */
11643 continue;
11644 }
11645 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011646 && (!dblquote || pc != '"')
11647 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011648 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011649 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011650 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011651 break;
11652 }
11653 /* fall through */
11654
11655 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011656 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011657 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011658 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011659
11660 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011661 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011662 needprompt = doprompt;
11663 break;
11664
11665 default:
11666 break;
11667 }
11668 STPUTC(pc, pout);
11669 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011670 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011671 STPUTC('\0', pout);
11672 psavelen = pout - (char *)stackblock();
11673 if (psavelen > 0) {
11674 pstr = grabstackstr(pout);
11675 setinputstring(pstr);
11676 }
11677 }
11678 nlpp = &bqlist;
11679 while (*nlpp)
11680 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011681 *nlpp = stzalloc(sizeof(**nlpp));
11682 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011683 parsebackquote = oldstyle;
11684
11685 if (oldstyle) {
11686 saveprompt = doprompt;
11687 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011688 }
11689
Eric Andersenc470f442003-07-28 09:56:35 +000011690 n = list(2);
11691
11692 if (oldstyle)
11693 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011694 else if (readtoken() != TRP)
11695 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011696
11697 (*nlpp)->n = n;
11698 if (oldstyle) {
11699 /*
11700 * Start reading from old file again, ignoring any pushed back
11701 * tokens left from the backquote parsing
11702 */
11703 popfile();
11704 tokpushback = 0;
11705 }
11706 while (stackblocksize() <= savelen)
11707 growstackblock();
11708 STARTSTACKSTR(out);
11709 if (str) {
11710 memcpy(out, str, savelen);
11711 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011712 INT_OFF;
11713 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011714 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011715 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011716 }
11717 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011718 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011719 if (arinest || dblquote)
11720 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11721 else
11722 USTPUTC(CTLBACKQ, out);
11723 if (oldstyle)
11724 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011725 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011726}
11727
Mike Frysinger98c52642009-04-02 10:02:37 +000011728#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011729/*
11730 * Parse an arithmetic expansion (indicate start of one and set state)
11731 */
Eric Andersenc470f442003-07-28 09:56:35 +000011732parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011733 if (++arinest == 1) {
11734 prevsyntax = syntax;
11735 syntax = ARISYNTAX;
11736 USTPUTC(CTLARI, out);
11737 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011738 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011739 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011740 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011741 } else {
11742 /*
11743 * we collapse embedded arithmetic expansion to
11744 * parenthesis, which should be equivalent
11745 */
11746 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011747 }
Eric Andersenc470f442003-07-28 09:56:35 +000011748 goto parsearith_return;
11749}
11750#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011751
Eric Andersenc470f442003-07-28 09:56:35 +000011752} /* end of readtoken */
11753
Eric Andersencb57d552001-06-28 07:25:16 +000011754/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011755 * Read the next input token.
11756 * If the token is a word, we set backquotelist to the list of cmds in
11757 * backquotes. We set quoteflag to true if any part of the word was
11758 * quoted.
11759 * If the token is TREDIR, then we set redirnode to a structure containing
11760 * the redirection.
11761 * In all cases, the variable startlinno is set to the number of the line
11762 * on which the token starts.
11763 *
11764 * [Change comment: here documents and internal procedures]
11765 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11766 * word parsing code into a separate routine. In this case, readtoken
11767 * doesn't need to have any internal procedures, but parseword does.
11768 * We could also make parseoperator in essence the main routine, and
11769 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011770 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011771#define NEW_xxreadtoken
11772#ifdef NEW_xxreadtoken
11773/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011774static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011775 '\n', '(', ')', /* singles */
11776 '&', '|', ';', /* doubles */
11777 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011778};
Eric Andersencb57d552001-06-28 07:25:16 +000011779
Denis Vlasenko834dee72008-10-07 09:18:30 +000011780#define xxreadtoken_singles 3
11781#define xxreadtoken_doubles 3
11782
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011783static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011784 TNL, TLP, TRP, /* only single occurrence allowed */
11785 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11786 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011787 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011788};
11789
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011790static int
11791xxreadtoken(void)
11792{
11793 int c;
11794
11795 if (tokpushback) {
11796 tokpushback = 0;
11797 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011798 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011799 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011800 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011801 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011802 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011803 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011804 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011805
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011806 if (c == '#') {
11807 while ((c = pgetc()) != '\n' && c != PEOF)
11808 continue;
11809 pungetc();
11810 } else if (c == '\\') {
11811 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011812 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011813 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011814 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011815 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011816 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011817 } else {
11818 const char *p;
11819
11820 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11821 if (c != PEOF) {
11822 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011823 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011824 needprompt = doprompt;
11825 }
11826
11827 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011828 if (p == NULL)
11829 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011830
Denis Vlasenko834dee72008-10-07 09:18:30 +000011831 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11832 int cc = pgetc();
11833 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011834 p += xxreadtoken_doubles + 1;
11835 } else {
11836 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011837#if ENABLE_ASH_BASH_COMPAT
11838 if (c == '&' && cc == '>') /* &> */
11839 break; /* return readtoken1(...) */
11840#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011841 }
11842 }
11843 }
11844 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11845 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011846 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011847 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011848
11849 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011850}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011851#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011852#define RETURN(token) return lasttoken = token
11853static int
11854xxreadtoken(void)
11855{
11856 int c;
11857
11858 if (tokpushback) {
11859 tokpushback = 0;
11860 return lasttoken;
11861 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011862 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011863 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011864 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011865 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011866 switch (c) {
11867 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011868 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011869 continue;
11870 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011871 while ((c = pgetc()) != '\n' && c != PEOF)
11872 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011873 pungetc();
11874 continue;
11875 case '\\':
11876 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011877 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011878 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011879 continue;
11880 }
11881 pungetc();
11882 goto breakloop;
11883 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011884 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011885 needprompt = doprompt;
11886 RETURN(TNL);
11887 case PEOF:
11888 RETURN(TEOF);
11889 case '&':
11890 if (pgetc() == '&')
11891 RETURN(TAND);
11892 pungetc();
11893 RETURN(TBACKGND);
11894 case '|':
11895 if (pgetc() == '|')
11896 RETURN(TOR);
11897 pungetc();
11898 RETURN(TPIPE);
11899 case ';':
11900 if (pgetc() == ';')
11901 RETURN(TENDCASE);
11902 pungetc();
11903 RETURN(TSEMI);
11904 case '(':
11905 RETURN(TLP);
11906 case ')':
11907 RETURN(TRP);
11908 default:
11909 goto breakloop;
11910 }
11911 }
11912 breakloop:
11913 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11914#undef RETURN
11915}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011916#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917
11918static int
11919readtoken(void)
11920{
11921 int t;
11922#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011923 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011924#endif
11925
11926#if ENABLE_ASH_ALIAS
11927 top:
11928#endif
11929
11930 t = xxreadtoken();
11931
11932 /*
11933 * eat newlines
11934 */
11935 if (checkkwd & CHKNL) {
11936 while (t == TNL) {
11937 parseheredoc();
11938 t = xxreadtoken();
11939 }
11940 }
11941
11942 if (t != TWORD || quoteflag) {
11943 goto out;
11944 }
11945
11946 /*
11947 * check for keywords
11948 */
11949 if (checkkwd & CHKKWD) {
11950 const char *const *pp;
11951
11952 pp = findkwd(wordtext);
11953 if (pp) {
11954 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011955 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011956 goto out;
11957 }
11958 }
11959
11960 if (checkkwd & CHKALIAS) {
11961#if ENABLE_ASH_ALIAS
11962 struct alias *ap;
11963 ap = lookupalias(wordtext, 1);
11964 if (ap != NULL) {
11965 if (*ap->val) {
11966 pushstring(ap->val, ap);
11967 }
11968 goto top;
11969 }
11970#endif
11971 }
11972 out:
11973 checkkwd = 0;
11974#if DEBUG
11975 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011976 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011977 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011978 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011979#endif
11980 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011981}
11982
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011983static char
11984peektoken(void)
11985{
11986 int t;
11987
11988 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011989 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011990 return tokname_array[t][0];
11991}
Eric Andersencb57d552001-06-28 07:25:16 +000011992
11993/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011994 * Read and parse a command. Returns NODE_EOF on end of file.
11995 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011996 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011997static union node *
11998parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011999{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012000 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012001
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012002 tokpushback = 0;
12003 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012004 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012005 needprompt = 0;
12006 t = readtoken();
12007 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012008 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012009 if (t == TNL)
12010 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012011 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012012 return list(1);
12013}
12014
12015/*
12016 * Input any here documents.
12017 */
12018static void
12019parseheredoc(void)
12020{
12021 struct heredoc *here;
12022 union node *n;
12023
12024 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012025 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012026
12027 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012028 setprompt_if(needprompt, 2);
12029 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012030 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012031 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012032 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012033 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012034 n->narg.text = wordtext;
12035 n->narg.backquote = backquotelist;
12036 here->here->nhere.doc = n;
12037 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012038 }
Eric Andersencb57d552001-06-28 07:25:16 +000012039}
12040
12041
12042/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012043 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012044 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012045#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012046static const char *
12047expandstr(const char *ps)
12048{
12049 union node n;
12050
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012051 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12052 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012053 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012054 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012055 popfile();
12056
12057 n.narg.type = NARG;
12058 n.narg.next = NULL;
12059 n.narg.text = wordtext;
12060 n.narg.backquote = backquotelist;
12061
12062 expandarg(&n, NULL, 0);
12063 return stackblock();
12064}
12065#endif
12066
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012067/*
12068 * Execute a command or commands contained in a string.
12069 */
12070static int
12071evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012072{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012073 union node *n;
12074 struct stackmark smark;
12075 int skip;
12076
12077 setinputstring(s);
12078 setstackmark(&smark);
12079
12080 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012081 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012082 evaltree(n, 0);
12083 popstackmark(&smark);
12084 skip = evalskip;
12085 if (skip)
12086 break;
12087 }
12088 popfile();
12089
12090 skip &= mask;
12091 evalskip = skip;
12092 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012093}
12094
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012095/*
12096 * The eval command.
12097 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012098static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012099evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012100{
12101 char *p;
12102 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012103
Denis Vlasenko68404f12008-03-17 09:00:54 +000012104 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012105 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012106 argv += 2;
12107 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012108 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012109 for (;;) {
12110 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012111 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012112 if (p == NULL)
12113 break;
12114 STPUTC(' ', concat);
12115 }
12116 STPUTC('\0', concat);
12117 p = grabstackstr(concat);
12118 }
12119 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012120 }
12121 return exitstatus;
12122}
12123
12124/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012125 * Read and execute commands.
12126 * "Top" is nonzero for the top level command loop;
12127 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012128 */
12129static int
12130cmdloop(int top)
12131{
12132 union node *n;
12133 struct stackmark smark;
12134 int inter;
12135 int numeof = 0;
12136
12137 TRACE(("cmdloop(%d) called\n", top));
12138 for (;;) {
12139 int skip;
12140
12141 setstackmark(&smark);
12142#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012143 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012144 showjobs(stderr, SHOW_CHANGED);
12145#endif
12146 inter = 0;
12147 if (iflag && top) {
12148 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012149 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012150 }
12151 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012152#if DEBUG
12153 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012154 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012155#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012156 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012157 if (!top || numeof >= 50)
12158 break;
12159 if (!stoppedjobs()) {
12160 if (!Iflag)
12161 break;
12162 out2str("\nUse \"exit\" to leave shell.\n");
12163 }
12164 numeof++;
12165 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012166 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12167 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012168 numeof = 0;
12169 evaltree(n, 0);
12170 }
12171 popstackmark(&smark);
12172 skip = evalskip;
12173
12174 if (skip) {
12175 evalskip = 0;
12176 return skip & SKIPEVAL;
12177 }
12178 }
12179 return 0;
12180}
12181
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012182/*
12183 * Take commands from a file. To be compatible we should do a path
12184 * search for the file, which is necessary to find sub-commands.
12185 */
12186static char *
12187find_dot_file(char *name)
12188{
12189 char *fullname;
12190 const char *path = pathval();
12191 struct stat statb;
12192
12193 /* don't try this for absolute or relative paths */
12194 if (strchr(name, '/'))
12195 return name;
12196
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012197 /* IIRC standards do not say whether . is to be searched.
12198 * And it is even smaller this way, making it unconditional for now:
12199 */
12200 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12201 fullname = name;
12202 goto try_cur_dir;
12203 }
12204
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012205 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012206 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012207 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12208 /*
12209 * Don't bother freeing here, since it will
12210 * be freed by the caller.
12211 */
12212 return fullname;
12213 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012214 if (fullname != name)
12215 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012216 }
12217
12218 /* not found in the PATH */
12219 ash_msg_and_raise_error("%s: not found", name);
12220 /* NOTREACHED */
12221}
12222
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012223static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012224dotcmd(int argc, char **argv)
12225{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012226 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012227 struct strlist *sp;
12228 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012229
12230 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012231 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012232
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012233 if (!argv[1]) {
12234 /* bash says: "bash: .: filename argument required" */
12235 return 2; /* bash compat */
12236 }
12237
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012238 /* "false; . empty_file; echo $?" should print 0, not 1: */
12239 exitstatus = 0;
12240
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012241 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012242
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012243 argv += 2;
12244 argc -= 2;
12245 if (argc) { /* argc > 0, argv[0] != NULL */
12246 saveparam = shellparam;
12247 shellparam.malloced = 0;
12248 shellparam.nparam = argc;
12249 shellparam.p = argv;
12250 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012251
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012252 setinputfile(fullname, INPUT_PUSH_FILE);
12253 commandname = fullname;
12254 cmdloop(0);
12255 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012256
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012257 if (argc) {
12258 freeparam(&shellparam);
12259 shellparam = saveparam;
12260 };
12261
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012262 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012263}
12264
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012265static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012266exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012267{
12268 if (stoppedjobs())
12269 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012270 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012271 exitstatus = number(argv[1]);
12272 raise_exception(EXEXIT);
12273 /* NOTREACHED */
12274}
12275
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012276/*
12277 * Read a file containing shell functions.
12278 */
12279static void
12280readcmdfile(char *name)
12281{
12282 setinputfile(name, INPUT_PUSH_FILE);
12283 cmdloop(0);
12284 popfile();
12285}
12286
12287
Denis Vlasenkocc571512007-02-23 21:10:35 +000012288/* ============ find_command inplementation */
12289
12290/*
12291 * Resolve a command name. If you change this routine, you may have to
12292 * change the shellexec routine as well.
12293 */
12294static void
12295find_command(char *name, struct cmdentry *entry, int act, const char *path)
12296{
12297 struct tblentry *cmdp;
12298 int idx;
12299 int prev;
12300 char *fullname;
12301 struct stat statb;
12302 int e;
12303 int updatetbl;
12304 struct builtincmd *bcmd;
12305
12306 /* If name contains a slash, don't use PATH or hash table */
12307 if (strchr(name, '/') != NULL) {
12308 entry->u.index = -1;
12309 if (act & DO_ABS) {
12310 while (stat(name, &statb) < 0) {
12311#ifdef SYSV
12312 if (errno == EINTR)
12313 continue;
12314#endif
12315 entry->cmdtype = CMDUNKNOWN;
12316 return;
12317 }
12318 }
12319 entry->cmdtype = CMDNORMAL;
12320 return;
12321 }
12322
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012323/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012324
12325 updatetbl = (path == pathval());
12326 if (!updatetbl) {
12327 act |= DO_ALTPATH;
12328 if (strstr(path, "%builtin") != NULL)
12329 act |= DO_ALTBLTIN;
12330 }
12331
12332 /* If name is in the table, check answer will be ok */
12333 cmdp = cmdlookup(name, 0);
12334 if (cmdp != NULL) {
12335 int bit;
12336
12337 switch (cmdp->cmdtype) {
12338 default:
12339#if DEBUG
12340 abort();
12341#endif
12342 case CMDNORMAL:
12343 bit = DO_ALTPATH;
12344 break;
12345 case CMDFUNCTION:
12346 bit = DO_NOFUNC;
12347 break;
12348 case CMDBUILTIN:
12349 bit = DO_ALTBLTIN;
12350 break;
12351 }
12352 if (act & bit) {
12353 updatetbl = 0;
12354 cmdp = NULL;
12355 } else if (cmdp->rehash == 0)
12356 /* if not invalidated by cd, we're done */
12357 goto success;
12358 }
12359
12360 /* If %builtin not in path, check for builtin next */
12361 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012362 if (bcmd) {
12363 if (IS_BUILTIN_REGULAR(bcmd))
12364 goto builtin_success;
12365 if (act & DO_ALTPATH) {
12366 if (!(act & DO_ALTBLTIN))
12367 goto builtin_success;
12368 } else if (builtinloc <= 0) {
12369 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012370 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012371 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012372
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012373#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012374 {
12375 int applet_no = find_applet_by_name(name);
12376 if (applet_no >= 0) {
12377 entry->cmdtype = CMDNORMAL;
12378 entry->u.index = -2 - applet_no;
12379 return;
12380 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012381 }
12382#endif
12383
Denis Vlasenkocc571512007-02-23 21:10:35 +000012384 /* We have to search path. */
12385 prev = -1; /* where to start */
12386 if (cmdp && cmdp->rehash) { /* doing a rehash */
12387 if (cmdp->cmdtype == CMDBUILTIN)
12388 prev = builtinloc;
12389 else
12390 prev = cmdp->param.index;
12391 }
12392
12393 e = ENOENT;
12394 idx = -1;
12395 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012396 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012397 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012398 /* NB: code below will still use fullname
12399 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012400 idx++;
12401 if (pathopt) {
12402 if (prefix(pathopt, "builtin")) {
12403 if (bcmd)
12404 goto builtin_success;
12405 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012406 }
12407 if ((act & DO_NOFUNC)
12408 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012409 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012410 continue;
12411 }
12412 }
12413 /* if rehash, don't redo absolute path names */
12414 if (fullname[0] == '/' && idx <= prev) {
12415 if (idx < prev)
12416 continue;
12417 TRACE(("searchexec \"%s\": no change\n", name));
12418 goto success;
12419 }
12420 while (stat(fullname, &statb) < 0) {
12421#ifdef SYSV
12422 if (errno == EINTR)
12423 continue;
12424#endif
12425 if (errno != ENOENT && errno != ENOTDIR)
12426 e = errno;
12427 goto loop;
12428 }
12429 e = EACCES; /* if we fail, this will be the error */
12430 if (!S_ISREG(statb.st_mode))
12431 continue;
12432 if (pathopt) { /* this is a %func directory */
12433 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012434 /* NB: stalloc will return space pointed by fullname
12435 * (because we don't have any intervening allocations
12436 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012437 readcmdfile(fullname);
12438 cmdp = cmdlookup(name, 0);
12439 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12440 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12441 stunalloc(fullname);
12442 goto success;
12443 }
12444 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12445 if (!updatetbl) {
12446 entry->cmdtype = CMDNORMAL;
12447 entry->u.index = idx;
12448 return;
12449 }
12450 INT_OFF;
12451 cmdp = cmdlookup(name, 1);
12452 cmdp->cmdtype = CMDNORMAL;
12453 cmdp->param.index = idx;
12454 INT_ON;
12455 goto success;
12456 }
12457
12458 /* We failed. If there was an entry for this command, delete it */
12459 if (cmdp && updatetbl)
12460 delete_cmd_entry();
12461 if (act & DO_ERR)
12462 ash_msg("%s: %s", name, errmsg(e, "not found"));
12463 entry->cmdtype = CMDUNKNOWN;
12464 return;
12465
12466 builtin_success:
12467 if (!updatetbl) {
12468 entry->cmdtype = CMDBUILTIN;
12469 entry->u.cmd = bcmd;
12470 return;
12471 }
12472 INT_OFF;
12473 cmdp = cmdlookup(name, 1);
12474 cmdp->cmdtype = CMDBUILTIN;
12475 cmdp->param.cmd = bcmd;
12476 INT_ON;
12477 success:
12478 cmdp->rehash = 0;
12479 entry->cmdtype = cmdp->cmdtype;
12480 entry->u = cmdp->param;
12481}
12482
12483
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012484/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012485
Eric Andersencb57d552001-06-28 07:25:16 +000012486/*
Eric Andersencb57d552001-06-28 07:25:16 +000012487 * The trap builtin.
12488 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012489static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012490trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012491{
12492 char *action;
12493 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012494 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012495
Eric Andersenc470f442003-07-28 09:56:35 +000012496 nextopt(nullstr);
12497 ap = argptr;
12498 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012499 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012500 char *tr = trap_ptr[signo];
12501 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012502 /* note: bash adds "SIG", but only if invoked
12503 * as "bash". If called as "sh", or if set -o posix,
12504 * then it prints short signal names.
12505 * We are printing short names: */
12506 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012507 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012508 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012509 /* trap_ptr != trap only if we are in special-cased `trap` code.
12510 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012511 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012512 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012513 }
12514 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012515 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012516 if (trap_ptr != trap) {
12517 free(trap_ptr);
12518 trap_ptr = trap;
12519 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012520 */
Eric Andersencb57d552001-06-28 07:25:16 +000012521 return 0;
12522 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012523
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012524 action = NULL;
12525 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012526 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012527 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012528 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012529 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012530 if (signo < 0) {
12531 /* Mimic bash message exactly */
12532 ash_msg("%s: invalid signal specification", *ap);
12533 exitcode = 1;
12534 goto next;
12535 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012536 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012537 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012538 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012539 action = NULL;
12540 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012541 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012542 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012543 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012544 if (action)
12545 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012546 trap[signo] = action;
12547 if (signo != 0)
12548 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012549 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012550 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012551 ap++;
12552 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012553 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012554}
12555
Eric Andersenc470f442003-07-28 09:56:35 +000012556
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012557/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012558
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012559#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012560/*
12561 * Lists available builtins
12562 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012563static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012564helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012565{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012566 unsigned col;
12567 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012568
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012569 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012570 "Built-in commands:\n"
12571 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012572 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012573 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012574 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012575 if (col > 60) {
12576 out1fmt("\n");
12577 col = 0;
12578 }
12579 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012580#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012581 {
12582 const char *a = applet_names;
12583 while (*a) {
12584 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12585 if (col > 60) {
12586 out1fmt("\n");
12587 col = 0;
12588 }
12589 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012590 }
12591 }
12592#endif
12593 out1fmt("\n\n");
12594 return EXIT_SUCCESS;
12595}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012596#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012597
Eric Andersencb57d552001-06-28 07:25:16 +000012598/*
Eric Andersencb57d552001-06-28 07:25:16 +000012599 * The export and readonly commands.
12600 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012601static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012602exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012603{
12604 struct var *vp;
12605 char *name;
12606 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012607 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012608 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012609
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012610 if (nextopt("p") != 'p') {
12611 aptr = argptr;
12612 name = *aptr;
12613 if (name) {
12614 do {
12615 p = strchr(name, '=');
12616 if (p != NULL) {
12617 p++;
12618 } else {
12619 vp = *findvar(hashvar(name), name);
12620 if (vp) {
12621 vp->flags |= flag;
12622 continue;
12623 }
Eric Andersencb57d552001-06-28 07:25:16 +000012624 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012625 setvar(name, p, flag);
12626 } while ((name = *++aptr) != NULL);
12627 return 0;
12628 }
Eric Andersencb57d552001-06-28 07:25:16 +000012629 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012630 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012631 return 0;
12632}
12633
Eric Andersencb57d552001-06-28 07:25:16 +000012634/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012635 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012636 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012637static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012638unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012639{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012640 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012641
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012642 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012643 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012644 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012645}
12646
Eric Andersencb57d552001-06-28 07:25:16 +000012647/*
Eric Andersencb57d552001-06-28 07:25:16 +000012648 * The unset builtin command. We unset the function before we unset the
12649 * variable to allow a function to be unset when there is a readonly variable
12650 * with the same name.
12651 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012652static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012653unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012654{
12655 char **ap;
12656 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012657 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012658 int ret = 0;
12659
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012660 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012661 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012662 }
Eric Andersencb57d552001-06-28 07:25:16 +000012663
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012664 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012665 if (flag != 'f') {
12666 i = unsetvar(*ap);
12667 ret |= i;
12668 if (!(i & 2))
12669 continue;
12670 }
12671 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012672 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012673 }
Eric Andersenc470f442003-07-28 09:56:35 +000012674 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012675}
12676
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012677static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012678 ' ', offsetof(struct tms, tms_utime),
12679 '\n', offsetof(struct tms, tms_stime),
12680 ' ', offsetof(struct tms, tms_cutime),
12681 '\n', offsetof(struct tms, tms_cstime),
12682 0
12683};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012684static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012685timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012686{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012687 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012688 const unsigned char *p;
12689 struct tms buf;
12690
12691 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012692 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012693
12694 p = timescmd_str;
12695 do {
12696 t = *(clock_t *)(((char *) &buf) + p[1]);
12697 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012698 t = t % clk_tck;
12699 out1fmt("%lum%lu.%03lus%c",
12700 s / 60, s % 60,
12701 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012702 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012703 p += 2;
12704 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012705
Eric Andersencb57d552001-06-28 07:25:16 +000012706 return 0;
12707}
12708
Mike Frysinger98c52642009-04-02 10:02:37 +000012709#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012710/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012711 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012712 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012713 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012714 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012715 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012716static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012717letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012718{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012719 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012720
Denis Vlasenko68404f12008-03-17 09:00:54 +000012721 argv++;
12722 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012723 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012724 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012725 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012726 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012727
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012728 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012729}
Eric Andersenc470f442003-07-28 09:56:35 +000012730#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012731
Eric Andersenc470f442003-07-28 09:56:35 +000012732/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012733 * The read builtin. Options:
12734 * -r Do not interpret '\' specially
12735 * -s Turn off echo (tty only)
12736 * -n NCHARS Read NCHARS max
12737 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12738 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12739 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012740 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012741 * TODO: bash also has:
12742 * -a ARRAY Read into array[0],[1],etc
12743 * -d DELIM End on DELIM char, not newline
12744 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012745 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012746static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012747readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012748{
Denys Vlasenko73067272010-01-12 22:11:24 +010012749 char *opt_n = NULL;
12750 char *opt_p = NULL;
12751 char *opt_t = NULL;
12752 char *opt_u = NULL;
12753 int read_flags = 0;
12754 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012755 int i;
12756
Denys Vlasenko73067272010-01-12 22:11:24 +010012757 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012758 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012759 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012760 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012761 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012762 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012763 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012764 break;
12765 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012766 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012767 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012768 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012769 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012770 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012771 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012772 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012773 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012774 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012775 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012776 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012777 default:
12778 break;
12779 }
Eric Andersenc470f442003-07-28 09:56:35 +000012780 }
Paul Fox02eb9342005-09-07 16:56:02 +000012781
Denys Vlasenko03dad222010-01-12 23:29:57 +010012782 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012783 argptr,
12784 bltinlookup("IFS"), /* can be NULL */
12785 read_flags,
12786 opt_n,
12787 opt_p,
12788 opt_t,
12789 opt_u
12790 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012791
Denys Vlasenko73067272010-01-12 22:11:24 +010012792 if ((uintptr_t)r > 1)
12793 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012794
Denys Vlasenko73067272010-01-12 22:11:24 +010012795 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012796}
12797
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012798static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012799umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012800{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012801 static const char permuser[3] ALIGN1 = "ugo";
12802 static const char permmode[3] ALIGN1 = "rwx";
12803 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012804 S_IRUSR, S_IWUSR, S_IXUSR,
12805 S_IRGRP, S_IWGRP, S_IXGRP,
12806 S_IROTH, S_IWOTH, S_IXOTH
12807 };
12808
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012809 /* TODO: use bb_parse_mode() instead */
12810
Eric Andersenc470f442003-07-28 09:56:35 +000012811 char *ap;
12812 mode_t mask;
12813 int i;
12814 int symbolic_mode = 0;
12815
12816 while (nextopt("S") != '\0') {
12817 symbolic_mode = 1;
12818 }
12819
Denis Vlasenkob012b102007-02-19 22:43:01 +000012820 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012821 mask = umask(0);
12822 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012823 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012824
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012825 ap = *argptr;
12826 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012827 if (symbolic_mode) {
12828 char buf[18];
12829 char *p = buf;
12830
12831 for (i = 0; i < 3; i++) {
12832 int j;
12833
12834 *p++ = permuser[i];
12835 *p++ = '=';
12836 for (j = 0; j < 3; j++) {
12837 if ((mask & permmask[3 * i + j]) == 0) {
12838 *p++ = permmode[j];
12839 }
12840 }
12841 *p++ = ',';
12842 }
12843 *--p = 0;
12844 puts(buf);
12845 } else {
12846 out1fmt("%.4o\n", mask);
12847 }
12848 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012849 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012850 mask = 0;
12851 do {
12852 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012853 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012854 mask = (mask << 3) + (*ap - '0');
12855 } while (*++ap != '\0');
12856 umask(mask);
12857 } else {
12858 mask = ~mask & 0777;
12859 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012860 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012861 }
12862 umask(~mask & 0777);
12863 }
12864 }
12865 return 0;
12866}
12867
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012868static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012869ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012870{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012871 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012872}
12873
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012874/* ============ main() and helpers */
12875
12876/*
12877 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012878 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012879static void
12880exitshell(void)
12881{
12882 struct jmploc loc;
12883 char *p;
12884 int status;
12885
12886 status = exitstatus;
12887 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12888 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012889 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012890/* dash bug: it just does _exit(exitstatus) here
12891 * but we have to do setjobctl(0) first!
12892 * (bug is still not fixed in dash-0.5.3 - if you run dash
12893 * under Midnight Commander, on exit from dash MC is backgrounded) */
12894 status = exitstatus;
12895 goto out;
12896 }
12897 exception_handler = &loc;
12898 p = trap[0];
12899 if (p) {
12900 trap[0] = NULL;
12901 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012902 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012903 }
12904 flush_stdout_stderr();
12905 out:
12906 setjobctl(0);
12907 _exit(status);
12908 /* NOTREACHED */
12909}
12910
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012911static void
12912init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012913{
12914 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012915 /* we will never free this */
12916 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012917
12918 /* from trap.c: */
12919 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012920 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12921 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12922 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012923 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012924
12925 /* from var.c: */
12926 {
12927 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012928 const char *p;
12929 struct stat st1, st2;
12930
12931 initvar();
12932 for (envp = environ; envp && *envp; envp++) {
12933 if (strchr(*envp, '=')) {
12934 setvareq(*envp, VEXPORT|VTEXTFIXED);
12935 }
12936 }
12937
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012938 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012939
12940 p = lookupvar("PWD");
12941 if (p)
12942 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12943 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12944 p = '\0';
12945 setpwd(p, 0);
12946 }
12947}
12948
12949/*
12950 * Process the shell command line arguments.
12951 */
12952static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012953procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012954{
12955 int i;
12956 const char *xminusc;
12957 char **xargv;
12958
12959 xargv = argv;
12960 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012961 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012962 xargv++;
12963 for (i = 0; i < NOPTS; i++)
12964 optlist[i] = 2;
12965 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012966 if (options(1)) {
12967 /* it already printed err message */
12968 raise_exception(EXERROR);
12969 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012970 xargv = argptr;
12971 xminusc = minusc;
12972 if (*xargv == NULL) {
12973 if (xminusc)
12974 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12975 sflag = 1;
12976 }
12977 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12978 iflag = 1;
12979 if (mflag == 2)
12980 mflag = iflag;
12981 for (i = 0; i < NOPTS; i++)
12982 if (optlist[i] == 2)
12983 optlist[i] = 0;
12984#if DEBUG == 2
12985 debug = 1;
12986#endif
12987 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12988 if (xminusc) {
12989 minusc = *xargv++;
12990 if (*xargv)
12991 goto setarg0;
12992 } else if (!sflag) {
12993 setinputfile(*xargv, 0);
12994 setarg0:
12995 arg0 = *xargv++;
12996 commandname = arg0;
12997 }
12998
12999 shellparam.p = xargv;
13000#if ENABLE_ASH_GETOPTS
13001 shellparam.optind = 1;
13002 shellparam.optoff = -1;
13003#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013004 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013005 while (*xargv) {
13006 shellparam.nparam++;
13007 xargv++;
13008 }
13009 optschanged();
13010}
13011
13012/*
13013 * Read /etc/profile or .profile.
13014 */
13015static void
13016read_profile(const char *name)
13017{
13018 int skip;
13019
13020 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13021 return;
13022 skip = cmdloop(0);
13023 popfile();
13024 if (skip)
13025 exitshell();
13026}
13027
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013028/*
13029 * This routine is called when an error or an interrupt occurs in an
13030 * interactive shell and control is returned to the main command loop.
13031 */
13032static void
13033reset(void)
13034{
13035 /* from eval.c: */
13036 evalskip = 0;
13037 loopnest = 0;
13038 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013039 g_parsefile->left_in_buffer = 0;
13040 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013041 popallfiles();
13042 /* from parser.c: */
13043 tokpushback = 0;
13044 checkkwd = 0;
13045 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013046 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013047}
13048
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013049#if PROFILE
13050static short profile_buf[16384];
13051extern int etext();
13052#endif
13053
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013054/*
13055 * Main routine. We initialize things, parse the arguments, execute
13056 * profiles if we're a login shell, and then call cmdloop to execute
13057 * commands. The setjmp call sets up the location to jump to when an
13058 * exception occurs. When an exception occurs the variable "state"
13059 * is used to figure out how far we had gotten.
13060 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013061int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013062int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013063{
Mike Frysinger98c52642009-04-02 10:02:37 +000013064 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013065 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013066 struct jmploc jmploc;
13067 struct stackmark smark;
13068
Denis Vlasenko01631112007-12-16 17:20:38 +000013069 /* Initialize global data */
13070 INIT_G_misc();
13071 INIT_G_memstack();
13072 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013073#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013074 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013075#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013076 INIT_G_cmdtable();
13077
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013078#if PROFILE
13079 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13080#endif
13081
13082#if ENABLE_FEATURE_EDITING
13083 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13084#endif
13085 state = 0;
13086 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013087 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013088 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013089
13090 reset();
13091
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013092 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013093 if (e == EXERROR)
13094 exitstatus = 2;
13095 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013096 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013097 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013098 }
13099 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013100 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013101 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013102
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013103 popstackmark(&smark);
13104 FORCE_INT_ON; /* enable interrupts */
13105 if (s == 1)
13106 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013107 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013108 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013109 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013110 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013111 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013112 }
13113 exception_handler = &jmploc;
13114#if DEBUG
13115 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013116 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013117 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013118#endif
13119 rootpid = getpid();
13120
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013121 init();
13122 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013123 procargs(argv);
13124
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013125#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13126 if (iflag) {
13127 const char *hp = lookupvar("HISTFILE");
13128
13129 if (hp == NULL) {
13130 hp = lookupvar("HOME");
13131 if (hp != NULL) {
13132 char *defhp = concat_path_file(hp, ".ash_history");
13133 setvar("HISTFILE", defhp, 0);
13134 free(defhp);
13135 }
13136 }
13137 }
13138#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013139 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013140 isloginsh = 1;
13141 if (isloginsh) {
13142 state = 1;
13143 read_profile("/etc/profile");
13144 state1:
13145 state = 2;
13146 read_profile(".profile");
13147 }
13148 state2:
13149 state = 3;
13150 if (
13151#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013152 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013153#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013154 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013155 ) {
13156 shinit = lookupvar("ENV");
13157 if (shinit != NULL && *shinit != '\0') {
13158 read_profile(shinit);
13159 }
13160 }
13161 state3:
13162 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013163 if (minusc) {
13164 /* evalstring pushes parsefile stack.
13165 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013166 * is one of stacked source fds.
13167 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013168 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013169 // ^^ not necessary since now we special-case fd 0
13170 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013171 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013172 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013173
13174 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013175#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013176 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013177 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013178 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013179 line_input_state->hist_file = hp;
13180 }
13181#endif
13182 state4: /* XXX ??? - why isn't this before the "if" statement */
13183 cmdloop(1);
13184 }
13185#if PROFILE
13186 monitor(0);
13187#endif
13188#ifdef GPROF
13189 {
13190 extern void _mcleanup(void);
13191 _mcleanup();
13192 }
13193#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013194 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013195 exitshell();
13196 /* NOTREACHED */
13197}
13198
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013199
Eric Andersendf82f612001-06-28 07:46:40 +000013200/*-
13201 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013202 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013203 *
13204 * This code is derived from software contributed to Berkeley by
13205 * Kenneth Almquist.
13206 *
13207 * Redistribution and use in source and binary forms, with or without
13208 * modification, are permitted provided that the following conditions
13209 * are met:
13210 * 1. Redistributions of source code must retain the above copyright
13211 * notice, this list of conditions and the following disclaimer.
13212 * 2. Redistributions in binary form must reproduce the above copyright
13213 * notice, this list of conditions and the following disclaimer in the
13214 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013215 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013216 * may be used to endorse or promote products derived from this software
13217 * without specific prior written permission.
13218 *
13219 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13220 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13221 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13222 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13223 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13224 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13225 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13226 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13227 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13228 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13229 * SUCH DAMAGE.
13230 */