blob: 0b5111a391ca50a32f99ad33c3ec4bcd6ffb1ad7 [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 *
Denys Vlasenkof451b2c2012-06-09 02:06:57 +020026 * When debugging is on (DEBUG is 1 and "set -o debug" was executed),
27 * debugging info will be written to ./trace and a quit signal
28 * will generate a core dump.
Eric Andersenc470f442003-07-28 09:56:35 +000029 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000030#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000031/* Tweak debug output verbosity here */
32#define DEBUG_TIME 0
33#define DEBUG_PID 1
34#define DEBUG_SIG 1
35
Eric Andersenc470f442003-07-28 09:56:35 +000036#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000038#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000039
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
Denys Vlasenko20704f02011-03-23 17:59:27 +010045#include "busybox.h" /* for applet_names */
46#include "unicode.h"
47
Denys Vlasenko73067272010-01-12 22:11:24 +010048#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010049#if ENABLE_SH_MATH_SUPPORT
50# include "math.h"
51#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020052#if ENABLE_ASH_RANDOM_SUPPORT
53# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020054#else
55# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020056#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000057
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020058#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010059#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000060/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020061# undef CONFIG_FEATURE_SH_STANDALONE
62# undef ENABLE_FEATURE_SH_STANDALONE
63# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010064# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020065# define ENABLE_FEATURE_SH_STANDALONE 0
66# define IF_FEATURE_SH_STANDALONE(...)
67# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000068#endif
69
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000070#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000071# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000072#endif
73
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010074#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000075# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000076#endif
77
Denys Vlasenko771f1992010-07-16 14:31:34 +020078//config:config ASH
79//config: bool "ash"
80//config: default y
81//config: depends on !NOMMU
82//config: help
83//config: Tha 'ash' shell adds about 60k in the default configuration and is
84//config: the most complete and most pedantically correct shell included with
85//config: busybox. This shell is actually a derivative of the Debian 'dash'
86//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
87//config: (written by Kenneth Almquist) from NetBSD.
88//config:
89//config:config ASH_BASH_COMPAT
90//config: bool "bash-compatible extensions"
91//config: default y
92//config: depends on ASH
93//config: help
94//config: Enable bash-compatible extensions.
95//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +010096//config:config ASH_IDLE_TIMEOUT
97//config: bool "Idle timeout variable"
98//config: default n
99//config: depends on ASH
100//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100101//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100102//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200103//config:config ASH_JOB_CONTROL
104//config: bool "Job control"
105//config: default y
106//config: depends on ASH
107//config: help
108//config: Enable job control in the ash shell.
109//config:
110//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100111//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200112//config: default y
113//config: depends on ASH
114//config: help
115//config: Enable alias support in the ash shell.
116//config:
117//config:config ASH_GETOPTS
118//config: bool "Builtin getopt to parse positional parameters"
119//config: default y
120//config: depends on ASH
121//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100122//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200123//config:
124//config:config ASH_BUILTIN_ECHO
125//config: bool "Builtin version of 'echo'"
126//config: default y
127//config: depends on ASH
128//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100129//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200130//config:
131//config:config ASH_BUILTIN_PRINTF
132//config: bool "Builtin version of 'printf'"
133//config: default y
134//config: depends on ASH
135//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100136//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200137//config:
138//config:config ASH_BUILTIN_TEST
139//config: bool "Builtin version of 'test'"
140//config: default y
141//config: depends on ASH
142//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100143//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200144//config:
145//config:config ASH_CMDCMD
146//config: bool "'command' command to override shell builtins"
147//config: default y
148//config: depends on ASH
149//config: help
150//config: Enable support for the ash 'command' builtin, which allows
151//config: you to run the specified command with the specified arguments,
152//config: even when there is an ash builtin command with the same name.
153//config:
154//config:config ASH_MAIL
155//config: bool "Check for new mail on interactive shells"
156//config: default n
157//config: depends on ASH
158//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100159//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200160//config:
161//config:config ASH_OPTIMIZE_FOR_SIZE
162//config: bool "Optimize for size instead of speed"
163//config: default y
164//config: depends on ASH
165//config: help
166//config: Compile ash for reduced size at the price of speed.
167//config:
168//config:config ASH_RANDOM_SUPPORT
169//config: bool "Pseudorandom generator and $RANDOM variable"
170//config: default y
171//config: depends on ASH
172//config: help
173//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
174//config: Each read of "$RANDOM" will generate a new pseudorandom value.
175//config: You can reset the generator by using a specified start value.
176//config: After "unset RANDOM" the generator will switch off and this
177//config: variable will no longer have special treatment.
178//config:
179//config:config ASH_EXPAND_PRMT
180//config: bool "Expand prompt string"
181//config: default y
182//config: depends on ASH
183//config: help
184//config: "PS#" may contain volatile content, such as backquote commands.
185//config: This option recreates the prompt string from the environment
186//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200187//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200188
Denys Vlasenko20704f02011-03-23 17:59:27 +0100189//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
190//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
191//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
192
193//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
194//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
195
Denis Vlasenkob012b102007-02-19 22:43:01 +0000196
Denis Vlasenko01631112007-12-16 17:20:38 +0000197/* ============ Hash table sizes. Configurable. */
198
199#define VTABSIZE 39
200#define ATABSIZE 39
201#define CMDTABLESIZE 31 /* should be prime */
202
203
Denis Vlasenkob012b102007-02-19 22:43:01 +0000204/* ============ Shell options */
205
206static const char *const optletters_optnames[] = {
207 "e" "errexit",
208 "f" "noglob",
209 "I" "ignoreeof",
210 "i" "interactive",
211 "m" "monitor",
212 "n" "noexec",
213 "s" "stdin",
214 "x" "xtrace",
215 "v" "verbose",
216 "C" "noclobber",
217 "a" "allexport",
218 "b" "notify",
219 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100220 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100221#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100222 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100223#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000224#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000225 ,"\0" "nolog"
226 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000227#endif
228};
229
Denys Vlasenko285ad152009-12-04 23:02:27 +0100230#define optletters(n) optletters_optnames[n][0]
231#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000232
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000233enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000234
Eric Andersenc470f442003-07-28 09:56:35 +0000235
Denis Vlasenkob012b102007-02-19 22:43:01 +0000236/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000237
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200238#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000239
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000240/*
Eric Andersenc470f442003-07-28 09:56:35 +0000241 * We enclose jmp_buf in a structure so that we can declare pointers to
242 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000243 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000244 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000245 * exception handlers, the user should save the value of handler on entry
246 * to an inner scope, set handler to point to a jmploc structure for the
247 * inner scope, and restore handler on exit from the scope.
248 */
Eric Andersenc470f442003-07-28 09:56:35 +0000249struct jmploc {
250 jmp_buf loc;
251};
Denis Vlasenko01631112007-12-16 17:20:38 +0000252
253struct globals_misc {
254 /* pid of main shell */
255 int rootpid;
256 /* shell level: 0 for the main shell, 1 for its children, and so on */
257 int shlvl;
258#define rootshell (!shlvl)
259 char *minusc; /* argument to -c option */
260
261 char *curdir; // = nullstr; /* current working directory */
262 char *physdir; // = nullstr; /* physical working directory */
263
264 char *arg0; /* value of $0 */
265
266 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000267
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200268 volatile int suppress_int; /* counter */
269 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000270 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200271 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000272 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000273 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000274#define EXINT 0 /* SIGINT received */
275#define EXERROR 1 /* a generic error */
276#define EXSHELLPROC 2 /* execute a shell procedure */
277#define EXEXEC 3 /* command execution failed */
278#define EXEXIT 4 /* exit the shell */
279#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000280
Denis Vlasenko01631112007-12-16 17:20:38 +0000281 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000282 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000283
284 char optlist[NOPTS];
285#define eflag optlist[0]
286#define fflag optlist[1]
287#define Iflag optlist[2]
288#define iflag optlist[3]
289#define mflag optlist[4]
290#define nflag optlist[5]
291#define sflag optlist[6]
292#define xflag optlist[7]
293#define vflag optlist[8]
294#define Cflag optlist[9]
295#define aflag optlist[10]
296#define bflag optlist[11]
297#define uflag optlist[12]
298#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100299#if ENABLE_ASH_BASH_COMPAT
300# define pipefail optlist[14]
301#else
302# define pipefail 0
303#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000304#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100305# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
306# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000307#endif
308
309 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000310 /*
311 * Sigmode records the current value of the signal handlers for the various
312 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000313 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000314 */
315 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000316#define S_DFL 1 /* default signal handling (SIG_DFL) */
317#define S_CATCH 2 /* signal is caught */
318#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000319#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000320
Denis Vlasenko01631112007-12-16 17:20:38 +0000321 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000322 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200323 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000324 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200325 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000326
327 /* Rarely referenced stuff */
328#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200329 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000330#endif
331 pid_t backgndpid; /* pid of last background process */
332 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000333};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000334extern struct globals_misc *const ash_ptr_to_globals_misc;
335#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000336#define rootpid (G_misc.rootpid )
337#define shlvl (G_misc.shlvl )
338#define minusc (G_misc.minusc )
339#define curdir (G_misc.curdir )
340#define physdir (G_misc.physdir )
341#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000342#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000343#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200344#define suppress_int (G_misc.suppress_int )
345#define pending_int (G_misc.pending_int )
346#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000347#define isloginsh (G_misc.isloginsh )
348#define nullstr (G_misc.nullstr )
349#define optlist (G_misc.optlist )
350#define sigmode (G_misc.sigmode )
351#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200352#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000353#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200354#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200355#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000356#define backgndpid (G_misc.backgndpid )
357#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000358#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000359 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
360 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000361 curdir = nullstr; \
362 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200363 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000364} while (0)
365
366
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000367/* ============ DEBUG */
368#if DEBUG
369static void trace_printf(const char *fmt, ...);
370static void trace_vprintf(const char *fmt, va_list va);
371# define TRACE(param) trace_printf param
372# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000373# define close(fd) do { \
374 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000375 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200376 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000377 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000378} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000379#else
380# define TRACE(param)
381# define TRACEV(param)
382#endif
383
384
Denis Vlasenko559691a2008-10-05 18:39:31 +0000385/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000386#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
387
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100388#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
389#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
390
Denis Vlasenko559691a2008-10-05 18:39:31 +0000391static int isdigit_str9(const char *str)
392{
393 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
394 while (--maxlen && isdigit(*str))
395 str++;
396 return (*str == '\0');
397}
Denis Vlasenko01631112007-12-16 17:20:38 +0000398
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200399static const char *var_end(const char *var)
400{
401 while (*var)
402 if (*var++ == '=')
403 break;
404 return var;
405}
406
Denis Vlasenko559691a2008-10-05 18:39:31 +0000407
408/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100409
410static void exitshell(void) NORETURN;
411
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000412/*
Eric Andersen2870d962001-07-02 17:27:21 +0000413 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000414 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000415 * much more efficient and portable. (But hacking the kernel is so much
416 * more fun than worrying about efficiency and portability. :-))
417 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000418#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200419 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000420 xbarrier(); \
421} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000422
423/*
424 * Called to raise an exception. Since C doesn't include exceptions, we
425 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000426 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000427 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000428static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000429static void
430raise_exception(int e)
431{
432#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000433 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000434 abort();
435#endif
436 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000437 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000438 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000439}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000440#if DEBUG
441#define raise_exception(e) do { \
442 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
443 raise_exception(e); \
444} while (0)
445#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000446
447/*
448 * Called from trap.c when a SIGINT is received. (If the user specifies
449 * that SIGINT is to be trapped or ignored using the trap builtin, then
450 * this routine is not called.) Suppressint is nonzero when interrupts
451 * are held using the INT_OFF macro. (The test for iflag is just
452 * defensive programming.)
453 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000454static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000455static void
456raise_interrupt(void)
457{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000458 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000459
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200460 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000461 /* Signal is not automatically unmasked after it is raised,
462 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000463 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200464 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000465
Denis Vlasenko4b875702009-03-19 13:30:04 +0000466 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000467 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
468 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000469 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470 signal(SIGINT, SIG_DFL);
471 raise(SIGINT);
472 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000473 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000474 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000475 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000476 /* NOTREACHED */
477}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000478#if DEBUG
479#define raise_interrupt() do { \
480 TRACE(("raising interrupt on line %d\n", __LINE__)); \
481 raise_interrupt(); \
482} while (0)
483#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000484
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000485static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000486int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000487{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000488 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200489 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000490 raise_interrupt();
491 }
492}
493#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000494static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000495force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000496{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000497 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200498 suppress_int = 0;
499 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000500 raise_interrupt();
501}
502#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000503
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200504#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000505
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000506#define RESTORE_INT(v) do { \
507 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200508 suppress_int = (v); \
509 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000510 raise_interrupt(); \
511} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000512
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000513
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000514/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000515
Eric Andersenc470f442003-07-28 09:56:35 +0000516static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000517outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000518{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000519 INT_OFF;
520 fputs(p, file);
521 INT_ON;
522}
523
524static void
525flush_stdout_stderr(void)
526{
527 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100528 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000529 INT_ON;
530}
531
532static void
533outcslow(int c, FILE *dest)
534{
535 INT_OFF;
536 putc(c, dest);
537 fflush(dest);
538 INT_ON;
539}
540
541static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
542static int
543out1fmt(const char *fmt, ...)
544{
545 va_list ap;
546 int r;
547
548 INT_OFF;
549 va_start(ap, fmt);
550 r = vprintf(fmt, ap);
551 va_end(ap);
552 INT_ON;
553 return r;
554}
555
556static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
557static int
558fmtstr(char *outbuf, size_t length, const char *fmt, ...)
559{
560 va_list ap;
561 int ret;
562
563 va_start(ap, fmt);
564 INT_OFF;
565 ret = vsnprintf(outbuf, length, fmt, ap);
566 va_end(ap);
567 INT_ON;
568 return ret;
569}
570
571static void
572out1str(const char *p)
573{
574 outstr(p, stdout);
575}
576
577static void
578out2str(const char *p)
579{
580 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100581 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000582}
583
584
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000585/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000586
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000587/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100588#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200589#define CTLESC ((unsigned char)'\201') /* escape next character */
590#define CTLVAR ((unsigned char)'\202') /* variable defn */
591#define CTLENDVAR ((unsigned char)'\203')
592#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000593#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
594/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200595#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
596#define CTLENDARI ((unsigned char)'\207')
597#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100598#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000599
600/* variable substitution byte (follows CTLVAR) */
601#define VSTYPE 0x0f /* type of variable substitution */
602#define VSNUL 0x10 /* colon--treat the empty string as unset */
603#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
604
605/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000606#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
607#define VSMINUS 0x2 /* ${var-text} */
608#define VSPLUS 0x3 /* ${var+text} */
609#define VSQUESTION 0x4 /* ${var?message} */
610#define VSASSIGN 0x5 /* ${var=text} */
611#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
612#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
613#define VSTRIMLEFT 0x8 /* ${var#pattern} */
614#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
615#define VSLENGTH 0xa /* ${#var} */
616#if ENABLE_ASH_BASH_COMPAT
617#define VSSUBSTR 0xc /* ${var:position:length} */
618#define VSREPLACE 0xd /* ${var/pattern/replacement} */
619#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
620#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000621
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000622static const char dolatstr[] ALIGN1 = {
623 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
624};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000625
Denis Vlasenko559691a2008-10-05 18:39:31 +0000626#define NCMD 0
627#define NPIPE 1
628#define NREDIR 2
629#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000630#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000631#define NAND 5
632#define NOR 6
633#define NSEMI 7
634#define NIF 8
635#define NWHILE 9
636#define NUNTIL 10
637#define NFOR 11
638#define NCASE 12
639#define NCLIST 13
640#define NDEFUN 14
641#define NARG 15
642#define NTO 16
643#if ENABLE_ASH_BASH_COMPAT
644#define NTO2 17
645#endif
646#define NCLOBBER 18
647#define NFROM 19
648#define NFROMTO 20
649#define NAPPEND 21
650#define NTOFD 22
651#define NFROMFD 23
652#define NHERE 24
653#define NXHERE 25
654#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000655#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000656
657union node;
658
659struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000660 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000661 union node *assign;
662 union node *args;
663 union node *redirect;
664};
665
666struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000667 smallint type;
668 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000669 struct nodelist *cmdlist;
670};
671
672struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000673 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000674 union node *n;
675 union node *redirect;
676};
677
678struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000679 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000680 union node *ch1;
681 union node *ch2;
682};
683
684struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000685 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000686 union node *test;
687 union node *ifpart;
688 union node *elsepart;
689};
690
691struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000692 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000693 union node *args;
694 union node *body;
695 char *var;
696};
697
698struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000699 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000700 union node *expr;
701 union node *cases;
702};
703
704struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000705 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000706 union node *next;
707 union node *pattern;
708 union node *body;
709};
710
711struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000712 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000713 union node *next;
714 char *text;
715 struct nodelist *backquote;
716};
717
Denis Vlasenko559691a2008-10-05 18:39:31 +0000718/* nfile and ndup layout must match!
719 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
720 * that it is actually NTO2 (>&file), and change its type.
721 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000722struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000723 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000724 union node *next;
725 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000726 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000727 union node *fname;
728 char *expfname;
729};
730
731struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000732 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000733 union node *next;
734 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000735 int dupfd;
736 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000737 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000738};
739
740struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000741 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000742 union node *next;
743 int fd;
744 union node *doc;
745};
746
747struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000748 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000749 union node *com;
750};
751
752union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000753 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000754 struct ncmd ncmd;
755 struct npipe npipe;
756 struct nredir nredir;
757 struct nbinary nbinary;
758 struct nif nif;
759 struct nfor nfor;
760 struct ncase ncase;
761 struct nclist nclist;
762 struct narg narg;
763 struct nfile nfile;
764 struct ndup ndup;
765 struct nhere nhere;
766 struct nnot nnot;
767};
768
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200769/*
770 * NODE_EOF is returned by parsecmd when it encounters an end of file.
771 * It must be distinct from NULL.
772 */
773#define NODE_EOF ((union node *) -1L)
774
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000775struct nodelist {
776 struct nodelist *next;
777 union node *n;
778};
779
780struct funcnode {
781 int count;
782 union node n;
783};
784
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000785/*
786 * Free a parse tree.
787 */
788static void
789freefunc(struct funcnode *f)
790{
791 if (f && --f->count < 0)
792 free(f);
793}
794
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000795
796/* ============ Debugging output */
797
798#if DEBUG
799
800static FILE *tracefile;
801
802static void
803trace_printf(const char *fmt, ...)
804{
805 va_list va;
806
807 if (debug != 1)
808 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000809 if (DEBUG_TIME)
810 fprintf(tracefile, "%u ", (int) time(NULL));
811 if (DEBUG_PID)
812 fprintf(tracefile, "[%u] ", (int) getpid());
813 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200814 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000815 va_start(va, fmt);
816 vfprintf(tracefile, fmt, va);
817 va_end(va);
818}
819
820static void
821trace_vprintf(const char *fmt, va_list va)
822{
823 if (debug != 1)
824 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000825 if (DEBUG_TIME)
826 fprintf(tracefile, "%u ", (int) time(NULL));
827 if (DEBUG_PID)
828 fprintf(tracefile, "[%u] ", (int) getpid());
829 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200830 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000831 vfprintf(tracefile, fmt, va);
832}
833
834static void
835trace_puts(const char *s)
836{
837 if (debug != 1)
838 return;
839 fputs(s, tracefile);
840}
841
842static void
843trace_puts_quoted(char *s)
844{
845 char *p;
846 char c;
847
848 if (debug != 1)
849 return;
850 putc('"', tracefile);
851 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100852 switch ((unsigned char)*p) {
853 case '\n': c = 'n'; goto backslash;
854 case '\t': c = 't'; goto backslash;
855 case '\r': c = 'r'; goto backslash;
856 case '\"': c = '\"'; goto backslash;
857 case '\\': c = '\\'; goto backslash;
858 case CTLESC: c = 'e'; goto backslash;
859 case CTLVAR: c = 'v'; goto backslash;
860 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
861 case CTLBACKQ: c = 'q'; goto backslash;
862 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000863 backslash:
864 putc('\\', tracefile);
865 putc(c, tracefile);
866 break;
867 default:
868 if (*p >= ' ' && *p <= '~')
869 putc(*p, tracefile);
870 else {
871 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100872 putc((*p >> 6) & 03, tracefile);
873 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000874 putc(*p & 07, tracefile);
875 }
876 break;
877 }
878 }
879 putc('"', tracefile);
880}
881
882static void
883trace_puts_args(char **ap)
884{
885 if (debug != 1)
886 return;
887 if (!*ap)
888 return;
889 while (1) {
890 trace_puts_quoted(*ap);
891 if (!*++ap) {
892 putc('\n', tracefile);
893 break;
894 }
895 putc(' ', tracefile);
896 }
897}
898
899static void
900opentrace(void)
901{
902 char s[100];
903#ifdef O_APPEND
904 int flags;
905#endif
906
907 if (debug != 1) {
908 if (tracefile)
909 fflush(tracefile);
910 /* leave open because libedit might be using it */
911 return;
912 }
913 strcpy(s, "./trace");
914 if (tracefile) {
915 if (!freopen(s, "a", tracefile)) {
916 fprintf(stderr, "Can't re-open %s\n", s);
917 debug = 0;
918 return;
919 }
920 } else {
921 tracefile = fopen(s, "a");
922 if (tracefile == NULL) {
923 fprintf(stderr, "Can't open %s\n", s);
924 debug = 0;
925 return;
926 }
927 }
928#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000929 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000930 if (flags >= 0)
931 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
932#endif
933 setlinebuf(tracefile);
934 fputs("\nTracing started.\n", tracefile);
935}
936
937static void
938indent(int amount, char *pfx, FILE *fp)
939{
940 int i;
941
942 for (i = 0; i < amount; i++) {
943 if (pfx && i == amount - 1)
944 fputs(pfx, fp);
945 putc('\t', fp);
946 }
947}
948
949/* little circular references here... */
950static void shtree(union node *n, int ind, char *pfx, FILE *fp);
951
952static void
953sharg(union node *arg, FILE *fp)
954{
955 char *p;
956 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100957 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000958
959 if (arg->type != NARG) {
960 out1fmt("<node type %d>\n", arg->type);
961 abort();
962 }
963 bqlist = arg->narg.backquote;
964 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100965 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000966 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700967 p++;
968 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000969 break;
970 case CTLVAR:
971 putc('$', fp);
972 putc('{', fp);
973 subtype = *++p;
974 if (subtype == VSLENGTH)
975 putc('#', fp);
976
Dan Fandrich77d48722010-09-07 23:38:28 -0700977 while (*p != '=') {
978 putc(*p, fp);
979 p++;
980 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000981
982 if (subtype & VSNUL)
983 putc(':', fp);
984
985 switch (subtype & VSTYPE) {
986 case VSNORMAL:
987 putc('}', fp);
988 break;
989 case VSMINUS:
990 putc('-', fp);
991 break;
992 case VSPLUS:
993 putc('+', fp);
994 break;
995 case VSQUESTION:
996 putc('?', fp);
997 break;
998 case VSASSIGN:
999 putc('=', fp);
1000 break;
1001 case VSTRIMLEFT:
1002 putc('#', fp);
1003 break;
1004 case VSTRIMLEFTMAX:
1005 putc('#', fp);
1006 putc('#', fp);
1007 break;
1008 case VSTRIMRIGHT:
1009 putc('%', fp);
1010 break;
1011 case VSTRIMRIGHTMAX:
1012 putc('%', fp);
1013 putc('%', fp);
1014 break;
1015 case VSLENGTH:
1016 break;
1017 default:
1018 out1fmt("<subtype %d>", subtype);
1019 }
1020 break;
1021 case CTLENDVAR:
1022 putc('}', fp);
1023 break;
1024 case CTLBACKQ:
1025 case CTLBACKQ|CTLQUOTE:
1026 putc('$', fp);
1027 putc('(', fp);
1028 shtree(bqlist->n, -1, NULL, fp);
1029 putc(')', fp);
1030 break;
1031 default:
1032 putc(*p, fp);
1033 break;
1034 }
1035 }
1036}
1037
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001038static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001039shcmd(union node *cmd, FILE *fp)
1040{
1041 union node *np;
1042 int first;
1043 const char *s;
1044 int dftfd;
1045
1046 first = 1;
1047 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001048 if (!first)
1049 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001050 sharg(np, fp);
1051 first = 0;
1052 }
1053 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001054 if (!first)
1055 putc(' ', fp);
1056 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001057 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001058 case NTO: s = ">>"+1; dftfd = 1; break;
1059 case NCLOBBER: s = ">|"; dftfd = 1; break;
1060 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001061#if ENABLE_ASH_BASH_COMPAT
1062 case NTO2:
1063#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001064 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001065 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001066 case NFROMFD: s = "<&"; break;
1067 case NFROMTO: s = "<>"; break;
1068 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001069 }
1070 if (np->nfile.fd != dftfd)
1071 fprintf(fp, "%d", np->nfile.fd);
1072 fputs(s, fp);
1073 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1074 fprintf(fp, "%d", np->ndup.dupfd);
1075 } else {
1076 sharg(np->nfile.fname, fp);
1077 }
1078 first = 0;
1079 }
1080}
1081
1082static void
1083shtree(union node *n, int ind, char *pfx, FILE *fp)
1084{
1085 struct nodelist *lp;
1086 const char *s;
1087
1088 if (n == NULL)
1089 return;
1090
1091 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001092
1093 if (n == NODE_EOF) {
1094 fputs("<EOF>", fp);
1095 return;
1096 }
1097
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001098 switch (n->type) {
1099 case NSEMI:
1100 s = "; ";
1101 goto binop;
1102 case NAND:
1103 s = " && ";
1104 goto binop;
1105 case NOR:
1106 s = " || ";
1107 binop:
1108 shtree(n->nbinary.ch1, ind, NULL, fp);
1109 /* if (ind < 0) */
1110 fputs(s, fp);
1111 shtree(n->nbinary.ch2, ind, NULL, fp);
1112 break;
1113 case NCMD:
1114 shcmd(n, fp);
1115 if (ind >= 0)
1116 putc('\n', fp);
1117 break;
1118 case NPIPE:
1119 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001120 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001121 if (lp->next)
1122 fputs(" | ", fp);
1123 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001124 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001125 fputs(" &", fp);
1126 if (ind >= 0)
1127 putc('\n', fp);
1128 break;
1129 default:
1130 fprintf(fp, "<node type %d>", n->type);
1131 if (ind >= 0)
1132 putc('\n', fp);
1133 break;
1134 }
1135}
1136
1137static void
1138showtree(union node *n)
1139{
1140 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001141 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001142}
1143
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001144#endif /* DEBUG */
1145
1146
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001147/* ============ Parser data */
1148
1149/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001150 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1151 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001152struct strlist {
1153 struct strlist *next;
1154 char *text;
1155};
1156
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001157struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001158
Denis Vlasenkob012b102007-02-19 22:43:01 +00001159struct strpush {
1160 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001161 char *prev_string;
1162 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001163#if ENABLE_ASH_ALIAS
1164 struct alias *ap; /* if push was associated with an alias */
1165#endif
1166 char *string; /* remember the string since it may change */
1167};
1168
1169struct parsefile {
1170 struct parsefile *prev; /* preceding file on stack */
1171 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001172 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001173 int left_in_line; /* number of chars left in this line */
1174 int left_in_buffer; /* number of chars left in this buffer past the line */
1175 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001176 char *buf; /* input buffer */
1177 struct strpush *strpush; /* for pushing strings at this level */
1178 struct strpush basestrpush; /* so pushing one is fast */
1179};
1180
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001181static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001182static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001183static int startlinno; /* line # where last token started */
1184static char *commandname; /* currently executing command */
1185static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001186static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001187
1188
1189/* ============ Message printing */
1190
1191static void
1192ash_vmsg(const char *msg, va_list ap)
1193{
1194 fprintf(stderr, "%s: ", arg0);
1195 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001196 if (strcmp(arg0, commandname))
1197 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001198 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001199 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001200 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001201 vfprintf(stderr, msg, ap);
1202 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001203}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001204
1205/*
1206 * Exverror is called to raise the error exception. If the second argument
1207 * is not NULL then error prints an error message using printf style
1208 * formatting. It then raises the error exception.
1209 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001210static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001211static void
1212ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001213{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001214#if DEBUG
1215 if (msg) {
1216 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1217 TRACEV((msg, ap));
1218 TRACE(("\") pid=%d\n", getpid()));
1219 } else
1220 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1221 if (msg)
1222#endif
1223 ash_vmsg(msg, ap);
1224
1225 flush_stdout_stderr();
1226 raise_exception(cond);
1227 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001228}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001229
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001230static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001231static void
1232ash_msg_and_raise_error(const char *msg, ...)
1233{
1234 va_list ap;
1235
1236 va_start(ap, msg);
1237 ash_vmsg_and_raise(EXERROR, msg, ap);
1238 /* NOTREACHED */
1239 va_end(ap);
1240}
1241
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001242static void raise_error_syntax(const char *) NORETURN;
1243static void
1244raise_error_syntax(const char *msg)
1245{
1246 ash_msg_and_raise_error("syntax error: %s", msg);
1247 /* NOTREACHED */
1248}
1249
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001250static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001251static void
1252ash_msg_and_raise(int cond, const char *msg, ...)
1253{
1254 va_list ap;
1255
1256 va_start(ap, msg);
1257 ash_vmsg_and_raise(cond, msg, ap);
1258 /* NOTREACHED */
1259 va_end(ap);
1260}
1261
1262/*
1263 * error/warning routines for external builtins
1264 */
1265static void
1266ash_msg(const char *fmt, ...)
1267{
1268 va_list ap;
1269
1270 va_start(ap, fmt);
1271 ash_vmsg(fmt, ap);
1272 va_end(ap);
1273}
1274
1275/*
1276 * Return a string describing an error. The returned string may be a
1277 * pointer to a static buffer that will be overwritten on the next call.
1278 * Action describes the operation that got the error.
1279 */
1280static const char *
1281errmsg(int e, const char *em)
1282{
1283 if (e == ENOENT || e == ENOTDIR) {
1284 return em;
1285 }
1286 return strerror(e);
1287}
1288
1289
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001290/* ============ Memory allocation */
1291
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001292#if 0
1293/* I consider these wrappers nearly useless:
1294 * ok, they return you to nearest exception handler, but
1295 * how much memory do you leak in the process, making
1296 * memory starvation worse?
1297 */
1298static void *
1299ckrealloc(void * p, size_t nbytes)
1300{
1301 p = realloc(p, nbytes);
1302 if (!p)
1303 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1304 return p;
1305}
1306
1307static void *
1308ckmalloc(size_t nbytes)
1309{
1310 return ckrealloc(NULL, nbytes);
1311}
1312
1313static void *
1314ckzalloc(size_t nbytes)
1315{
1316 return memset(ckmalloc(nbytes), 0, nbytes);
1317}
1318
1319static char *
1320ckstrdup(const char *s)
1321{
1322 char *p = strdup(s);
1323 if (!p)
1324 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1325 return p;
1326}
1327#else
1328/* Using bbox equivalents. They exit if out of memory */
1329# define ckrealloc xrealloc
1330# define ckmalloc xmalloc
1331# define ckzalloc xzalloc
1332# define ckstrdup xstrdup
1333#endif
1334
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001335/*
1336 * It appears that grabstackstr() will barf with such alignments
1337 * because stalloc() will return a string allocated in a new stackblock.
1338 */
1339#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1340enum {
1341 /* Most machines require the value returned from malloc to be aligned
1342 * in some way. The following macro will get this right
1343 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001344 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001345 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001346 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001347};
1348
1349struct stack_block {
1350 struct stack_block *prev;
1351 char space[MINSIZE];
1352};
1353
1354struct stackmark {
1355 struct stack_block *stackp;
1356 char *stacknxt;
1357 size_t stacknleft;
1358 struct stackmark *marknext;
1359};
1360
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001361
Denis Vlasenko01631112007-12-16 17:20:38 +00001362struct globals_memstack {
1363 struct stack_block *g_stackp; // = &stackbase;
1364 struct stackmark *markp;
1365 char *g_stacknxt; // = stackbase.space;
1366 char *sstrend; // = stackbase.space + MINSIZE;
1367 size_t g_stacknleft; // = MINSIZE;
1368 int herefd; // = -1;
1369 struct stack_block stackbase;
1370};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001371extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1372#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001373#define g_stackp (G_memstack.g_stackp )
1374#define markp (G_memstack.markp )
1375#define g_stacknxt (G_memstack.g_stacknxt )
1376#define sstrend (G_memstack.sstrend )
1377#define g_stacknleft (G_memstack.g_stacknleft)
1378#define herefd (G_memstack.herefd )
1379#define stackbase (G_memstack.stackbase )
1380#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001381 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1382 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001383 g_stackp = &stackbase; \
1384 g_stacknxt = stackbase.space; \
1385 g_stacknleft = MINSIZE; \
1386 sstrend = stackbase.space + MINSIZE; \
1387 herefd = -1; \
1388} while (0)
1389
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001390
Denis Vlasenko01631112007-12-16 17:20:38 +00001391#define stackblock() ((void *)g_stacknxt)
1392#define stackblocksize() g_stacknleft
1393
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001394/*
1395 * Parse trees for commands are allocated in lifo order, so we use a stack
1396 * to make this more efficient, and also to avoid all sorts of exception
1397 * handling code to handle interrupts in the middle of a parse.
1398 *
1399 * The size 504 was chosen because the Ultrix malloc handles that size
1400 * well.
1401 */
1402static void *
1403stalloc(size_t nbytes)
1404{
1405 char *p;
1406 size_t aligned;
1407
1408 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001409 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001410 size_t len;
1411 size_t blocksize;
1412 struct stack_block *sp;
1413
1414 blocksize = aligned;
1415 if (blocksize < MINSIZE)
1416 blocksize = MINSIZE;
1417 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1418 if (len < blocksize)
1419 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1420 INT_OFF;
1421 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001422 sp->prev = g_stackp;
1423 g_stacknxt = sp->space;
1424 g_stacknleft = blocksize;
1425 sstrend = g_stacknxt + blocksize;
1426 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001427 INT_ON;
1428 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001429 p = g_stacknxt;
1430 g_stacknxt += aligned;
1431 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001432 return p;
1433}
1434
Denis Vlasenko597906c2008-02-20 16:38:54 +00001435static void *
1436stzalloc(size_t nbytes)
1437{
1438 return memset(stalloc(nbytes), 0, nbytes);
1439}
1440
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001441static void
1442stunalloc(void *p)
1443{
1444#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001445 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001446 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001447 abort();
1448 }
1449#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001450 g_stacknleft += g_stacknxt - (char *)p;
1451 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001452}
1453
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001454/*
1455 * Like strdup but works with the ash stack.
1456 */
1457static char *
1458ststrdup(const char *p)
1459{
1460 size_t len = strlen(p) + 1;
1461 return memcpy(stalloc(len), p, len);
1462}
1463
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001464static void
1465setstackmark(struct stackmark *mark)
1466{
Denis Vlasenko01631112007-12-16 17:20:38 +00001467 mark->stackp = g_stackp;
1468 mark->stacknxt = g_stacknxt;
1469 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001470 mark->marknext = markp;
1471 markp = mark;
1472}
1473
1474static void
1475popstackmark(struct stackmark *mark)
1476{
1477 struct stack_block *sp;
1478
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001479 if (!mark->stackp)
1480 return;
1481
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001482 INT_OFF;
1483 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001484 while (g_stackp != mark->stackp) {
1485 sp = g_stackp;
1486 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001487 free(sp);
1488 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001489 g_stacknxt = mark->stacknxt;
1490 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001491 sstrend = mark->stacknxt + mark->stacknleft;
1492 INT_ON;
1493}
1494
1495/*
1496 * When the parser reads in a string, it wants to stick the string on the
1497 * stack and only adjust the stack pointer when it knows how big the
1498 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1499 * of space on top of the stack and stackblocklen returns the length of
1500 * this block. Growstackblock will grow this space by at least one byte,
1501 * possibly moving it (like realloc). Grabstackblock actually allocates the
1502 * part of the block that has been used.
1503 */
1504static void
1505growstackblock(void)
1506{
1507 size_t newlen;
1508
Denis Vlasenko01631112007-12-16 17:20:38 +00001509 newlen = g_stacknleft * 2;
1510 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001511 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1512 if (newlen < 128)
1513 newlen += 128;
1514
Denis Vlasenko01631112007-12-16 17:20:38 +00001515 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001516 struct stack_block *oldstackp;
1517 struct stackmark *xmark;
1518 struct stack_block *sp;
1519 struct stack_block *prevstackp;
1520 size_t grosslen;
1521
1522 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001523 oldstackp = g_stackp;
1524 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001525 prevstackp = sp->prev;
1526 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1527 sp = ckrealloc(sp, grosslen);
1528 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001529 g_stackp = sp;
1530 g_stacknxt = sp->space;
1531 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001532 sstrend = sp->space + newlen;
1533
1534 /*
1535 * Stack marks pointing to the start of the old block
1536 * must be relocated to point to the new block
1537 */
1538 xmark = markp;
1539 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001540 xmark->stackp = g_stackp;
1541 xmark->stacknxt = g_stacknxt;
1542 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001543 xmark = xmark->marknext;
1544 }
1545 INT_ON;
1546 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001547 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001548 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001549 char *p = stalloc(newlen);
1550
1551 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001552 g_stacknxt = memcpy(p, oldspace, oldlen);
1553 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001554 }
1555}
1556
1557static void
1558grabstackblock(size_t len)
1559{
1560 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001561 g_stacknxt += len;
1562 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001563}
1564
1565/*
1566 * The following routines are somewhat easier to use than the above.
1567 * The user declares a variable of type STACKSTR, which may be declared
1568 * to be a register. The macro STARTSTACKSTR initializes things. Then
1569 * the user uses the macro STPUTC to add characters to the string. In
1570 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1571 * grown as necessary. When the user is done, she can just leave the
1572 * string there and refer to it using stackblock(). Or she can allocate
1573 * the space for it using grabstackstr(). If it is necessary to allow
1574 * someone else to use the stack temporarily and then continue to grow
1575 * the string, the user should use grabstack to allocate the space, and
1576 * then call ungrabstr(p) to return to the previous mode of operation.
1577 *
1578 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1579 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1580 * is space for at least one character.
1581 */
1582static void *
1583growstackstr(void)
1584{
1585 size_t len = stackblocksize();
1586 if (herefd >= 0 && len >= 1024) {
1587 full_write(herefd, stackblock(), len);
1588 return stackblock();
1589 }
1590 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001591 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001592}
1593
1594/*
1595 * Called from CHECKSTRSPACE.
1596 */
1597static char *
1598makestrspace(size_t newlen, char *p)
1599{
Denis Vlasenko01631112007-12-16 17:20:38 +00001600 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001601 size_t size = stackblocksize();
1602
1603 for (;;) {
1604 size_t nleft;
1605
1606 size = stackblocksize();
1607 nleft = size - len;
1608 if (nleft >= newlen)
1609 break;
1610 growstackblock();
1611 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001612 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001613}
1614
1615static char *
1616stack_nputstr(const char *s, size_t n, char *p)
1617{
1618 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001619 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001620 return p;
1621}
1622
1623static char *
1624stack_putstr(const char *s, char *p)
1625{
1626 return stack_nputstr(s, strlen(s), p);
1627}
1628
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001629static char *
1630_STPUTC(int c, char *p)
1631{
1632 if (p == sstrend)
1633 p = growstackstr();
1634 *p++ = c;
1635 return p;
1636}
1637
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001638#define STARTSTACKSTR(p) ((p) = stackblock())
1639#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001640#define CHECKSTRSPACE(n, p) do { \
1641 char *q = (p); \
1642 size_t l = (n); \
1643 size_t m = sstrend - q; \
1644 if (l > m) \
1645 (p) = makestrspace(l, q); \
1646} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001647#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001648#define STACKSTRNUL(p) do { \
1649 if ((p) == sstrend) \
1650 (p) = growstackstr(); \
1651 *(p) = '\0'; \
1652} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001653#define STUNPUTC(p) (--(p))
1654#define STTOPC(p) ((p)[-1])
1655#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001656
1657#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001658#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001659#define stackstrend() ((void *)sstrend)
1660
1661
1662/* ============ String helpers */
1663
1664/*
1665 * prefix -- see if pfx is a prefix of string.
1666 */
1667static char *
1668prefix(const char *string, const char *pfx)
1669{
1670 while (*pfx) {
1671 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001672 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001673 }
1674 return (char *) string;
1675}
1676
1677/*
1678 * Check for a valid number. This should be elsewhere.
1679 */
1680static int
1681is_number(const char *p)
1682{
1683 do {
1684 if (!isdigit(*p))
1685 return 0;
1686 } while (*++p != '\0');
1687 return 1;
1688}
1689
1690/*
1691 * Convert a string of digits to an integer, printing an error message on
1692 * failure.
1693 */
1694static int
1695number(const char *s)
1696{
1697 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001698 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001699 return atoi(s);
1700}
1701
1702/*
1703 * Produce a possibly single quoted string suitable as input to the shell.
1704 * The return string is allocated on the stack.
1705 */
1706static char *
1707single_quote(const char *s)
1708{
1709 char *p;
1710
1711 STARTSTACKSTR(p);
1712
1713 do {
1714 char *q;
1715 size_t len;
1716
1717 len = strchrnul(s, '\'') - s;
1718
1719 q = p = makestrspace(len + 3, p);
1720
1721 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001722 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723 *q++ = '\'';
1724 s += len;
1725
1726 STADJUST(q - p, p);
1727
Denys Vlasenkocd716832009-11-28 22:14:02 +01001728 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001730 len = 0;
1731 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001732
1733 q = p = makestrspace(len + 3, p);
1734
1735 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001736 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001737 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001738
1739 STADJUST(q - p, p);
1740 } while (*s);
1741
Denys Vlasenkocd716832009-11-28 22:14:02 +01001742 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001743
1744 return stackblock();
1745}
1746
1747
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001748/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749
1750static char **argptr; /* argument list for builtin commands */
1751static char *optionarg; /* set by nextopt (like getopt) */
1752static char *optptr; /* used by nextopt */
1753
1754/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001755 * XXX - should get rid of. Have all builtins use getopt(3).
1756 * The library getopt must have the BSD extension static variable
1757 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001758 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001759 * Standard option processing (a la getopt) for builtin routines.
1760 * The only argument that is passed to nextopt is the option string;
1761 * the other arguments are unnecessary. It returns the character,
1762 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001763 */
1764static int
1765nextopt(const char *optstring)
1766{
1767 char *p;
1768 const char *q;
1769 char c;
1770
1771 p = optptr;
1772 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001773 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001775 if (p == NULL)
1776 return '\0';
1777 if (*p != '-')
1778 return '\0';
1779 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780 return '\0';
1781 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001782 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001784 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001786 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001787 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001788 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001789 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001790 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791 if (*++q == ':')
1792 q++;
1793 }
1794 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001795 if (*p == '\0') {
1796 p = *argptr++;
1797 if (p == NULL)
1798 ash_msg_and_raise_error("no arg for -%c option", c);
1799 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800 optionarg = p;
1801 p = NULL;
1802 }
1803 optptr = p;
1804 return c;
1805}
1806
1807
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001808/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001809
Denis Vlasenko01631112007-12-16 17:20:38 +00001810/*
1811 * The parsefile structure pointed to by the global variable parsefile
1812 * contains information about the current file being read.
1813 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001814struct shparam {
1815 int nparam; /* # of positional parameters (without $0) */
1816#if ENABLE_ASH_GETOPTS
1817 int optind; /* next parameter to be processed by getopts */
1818 int optoff; /* used by getopts */
1819#endif
1820 unsigned char malloced; /* if parameter list dynamically allocated */
1821 char **p; /* parameter list */
1822};
1823
1824/*
1825 * Free the list of positional parameters.
1826 */
1827static void
1828freeparam(volatile struct shparam *param)
1829{
Denis Vlasenko01631112007-12-16 17:20:38 +00001830 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001831 char **ap, **ap1;
1832 ap = ap1 = param->p;
1833 while (*ap)
1834 free(*ap++);
1835 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001836 }
1837}
1838
1839#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001840static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001841#endif
1842
1843struct var {
1844 struct var *next; /* next entry in hash list */
1845 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001846 const char *var_text; /* name=value */
1847 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001848 /* the variable gets set/unset */
1849};
1850
1851struct localvar {
1852 struct localvar *next; /* next local variable in list */
1853 struct var *vp; /* the variable that was made local */
1854 int flags; /* saved flags */
1855 const char *text; /* saved text */
1856};
1857
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001858/* flags */
1859#define VEXPORT 0x01 /* variable is exported */
1860#define VREADONLY 0x02 /* variable cannot be modified */
1861#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1862#define VTEXTFIXED 0x08 /* text is statically allocated */
1863#define VSTACK 0x10 /* text is allocated on the stack */
1864#define VUNSET 0x20 /* the variable is not set */
1865#define VNOFUNC 0x40 /* don't call the callback function */
1866#define VNOSET 0x80 /* do not set variable - just readonly test */
1867#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001868#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001869# define VDYNAMIC 0x200 /* dynamic variable */
1870#else
1871# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001872#endif
1873
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001874
Denis Vlasenko01631112007-12-16 17:20:38 +00001875/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001876#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001877static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001878change_lc_all(const char *value)
1879{
1880 if (value && *value != '\0')
1881 setlocale(LC_ALL, value);
1882}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001883static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001884change_lc_ctype(const char *value)
1885{
1886 if (value && *value != '\0')
1887 setlocale(LC_CTYPE, value);
1888}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001889#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890#if ENABLE_ASH_MAIL
1891static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001892static void changemail(const char *var_value) FAST_FUNC;
1893#else
1894# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001895#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001896static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001897#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001898static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001899#endif
1900
Denis Vlasenko01631112007-12-16 17:20:38 +00001901static const struct {
1902 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001903 const char *var_text;
1904 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001905} varinit_data[] = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02001906 /*
1907 * Note: VEXPORT would not work correctly here for NOFORK applets:
1908 * some environment strings may be constant.
1909 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001910 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001912 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1913 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001914#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001915 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1916 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1917 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1918 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001919#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001920 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921#endif
1922#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001923 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001924#endif
1925#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001926 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1927 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001928#endif
1929#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001930 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001931#endif
1932};
1933
Denis Vlasenko0b769642008-07-24 07:54:57 +00001934struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001935
1936struct globals_var {
1937 struct shparam shellparam; /* $@ current positional parameters */
1938 struct redirtab *redirlist;
1939 int g_nullredirs;
1940 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1941 struct var *vartab[VTABSIZE];
1942 struct var varinit[ARRAY_SIZE(varinit_data)];
1943};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001944extern struct globals_var *const ash_ptr_to_globals_var;
1945#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001946#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001947//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001948#define g_nullredirs (G_var.g_nullredirs )
1949#define preverrout_fd (G_var.preverrout_fd)
1950#define vartab (G_var.vartab )
1951#define varinit (G_var.varinit )
1952#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001953 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001954 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1955 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001956 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001957 varinit[i].flags = varinit_data[i].flags; \
1958 varinit[i].var_text = varinit_data[i].var_text; \
1959 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001960 } \
1961} while (0)
1962
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001963#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001965# define vmail (&vifs)[1]
1966# define vmpath (&vmail)[1]
1967# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001968#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001969# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001971#define vps1 (&vpath)[1]
1972#define vps2 (&vps1)[1]
1973#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001974#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001975# define voptind (&vps4)[1]
1976# if ENABLE_ASH_RANDOM_SUPPORT
1977# define vrandom (&voptind)[1]
1978# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001979#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001980# if ENABLE_ASH_RANDOM_SUPPORT
1981# define vrandom (&vps4)[1]
1982# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001983#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984
1985/*
1986 * The following macros access the values of the above variables.
1987 * They have to skip over the name. They return the null string
1988 * for unset variables.
1989 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001990#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001991#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001992#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001993# define mailval() (vmail.var_text + 5)
1994# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001995# define mpathset() ((vmpath.flags & VUNSET) == 0)
1996#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001997#define pathval() (vpath.var_text + 5)
1998#define ps1val() (vps1.var_text + 4)
1999#define ps2val() (vps2.var_text + 4)
2000#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002001#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002002# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002003#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002004
Denis Vlasenko01631112007-12-16 17:20:38 +00002005#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002006static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002007getoptsreset(const char *value)
2008{
2009 shellparam.optind = number(value);
2010 shellparam.optoff = -1;
2011}
2012#endif
2013
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002014/*
2015 * Compares two strings up to the first = or '\0'. The first
2016 * string must be terminated by '='; the second may be terminated by
2017 * either '=' or '\0'.
2018 */
2019static int
2020varcmp(const char *p, const char *q)
2021{
2022 int c, d;
2023
2024 while ((c = *p) == (d = *q)) {
2025 if (!c || c == '=')
2026 goto out;
2027 p++;
2028 q++;
2029 }
2030 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002031 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002032 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002033 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002034 out:
2035 return c - d;
2036}
2037
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002038/*
2039 * Find the appropriate entry in the hash table from the name.
2040 */
2041static struct var **
2042hashvar(const char *p)
2043{
2044 unsigned hashval;
2045
2046 hashval = ((unsigned char) *p) << 4;
2047 while (*p && *p != '=')
2048 hashval += (unsigned char) *p++;
2049 return &vartab[hashval % VTABSIZE];
2050}
2051
2052static int
2053vpcmp(const void *a, const void *b)
2054{
2055 return varcmp(*(const char **)a, *(const char **)b);
2056}
2057
2058/*
2059 * This routine initializes the builtin variables.
2060 */
2061static void
2062initvar(void)
2063{
2064 struct var *vp;
2065 struct var *end;
2066 struct var **vpp;
2067
2068 /*
2069 * PS1 depends on uid
2070 */
2071#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002072 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002073#else
2074 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002075 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002076#endif
2077 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002078 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002080 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002081 vp->next = *vpp;
2082 *vpp = vp;
2083 } while (++vp < end);
2084}
2085
2086static struct var **
2087findvar(struct var **vpp, const char *name)
2088{
2089 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002090 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002091 break;
2092 }
2093 }
2094 return vpp;
2095}
2096
2097/*
2098 * Find the value of a variable. Returns NULL if not set.
2099 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002100static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002101lookupvar(const char *name)
2102{
2103 struct var *v;
2104
2105 v = *findvar(hashvar(name), name);
2106 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002107#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002108 /*
2109 * Dynamic variables are implemented roughly the same way they are
2110 * in bash. Namely, they're "special" so long as they aren't unset.
2111 * As soon as they're unset, they're no longer dynamic, and dynamic
2112 * lookup will no longer happen at that point. -- PFM.
2113 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002114 if (v->flags & VDYNAMIC)
2115 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002116#endif
2117 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002118 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119 }
2120 return NULL;
2121}
2122
2123/*
2124 * Search the environment of a builtin command.
2125 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002126static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002127bltinlookup(const char *name)
2128{
2129 struct strlist *sp;
2130
2131 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002132 if (varcmp(sp->text, name) == 0)
2133 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002134 }
2135 return lookupvar(name);
2136}
2137
2138/*
2139 * Same as setvar except that the variable and value are passed in
2140 * the first argument as name=value. Since the first argument will
2141 * be actually stored in the table, it should not be a string that
2142 * will go away.
2143 * Called with interrupts off.
2144 */
2145static void
2146setvareq(char *s, int flags)
2147{
2148 struct var *vp, **vpp;
2149
2150 vpp = hashvar(s);
2151 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2152 vp = *findvar(vpp, s);
2153 if (vp) {
2154 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2155 const char *n;
2156
2157 if (flags & VNOSAVE)
2158 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002159 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002160 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2161 }
2162
2163 if (flags & VNOSET)
2164 return;
2165
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002166 if (vp->var_func && !(flags & VNOFUNC))
2167 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002168
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002169 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2170 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002171
2172 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2173 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002174 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002175 if (flags & VNOSET)
2176 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002177 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002178 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002179 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002180 *vpp = vp;
2181 }
2182 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2183 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002184 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002185 vp->flags = flags;
2186}
2187
2188/*
2189 * Set the value of a variable. The flags argument is ored with the
2190 * flags of the variable. If val is NULL, the variable is unset.
2191 */
2192static void
2193setvar(const char *name, const char *val, int flags)
2194{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002195 const char *q;
2196 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002197 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002198 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002199 size_t vallen;
2200
2201 q = endofname(name);
2202 p = strchrnul(q, '=');
2203 namelen = p - name;
2204 if (!namelen || p != q)
2205 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2206 vallen = 0;
2207 if (val == NULL) {
2208 flags |= VUNSET;
2209 } else {
2210 vallen = strlen(val);
2211 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002212
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002213 INT_OFF;
2214 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002215 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002216 if (val) {
2217 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002218 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002219 }
2220 *p = '\0';
2221 setvareq(nameeq, flags | VNOSAVE);
2222 INT_ON;
2223}
2224
Denys Vlasenko03dad222010-01-12 23:29:57 +01002225static void FAST_FUNC
2226setvar2(const char *name, const char *val)
2227{
2228 setvar(name, val, 0);
2229}
2230
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231#if ENABLE_ASH_GETOPTS
2232/*
2233 * Safe version of setvar, returns 1 on success 0 on failure.
2234 */
2235static int
2236setvarsafe(const char *name, const char *val, int flags)
2237{
2238 int err;
2239 volatile int saveint;
2240 struct jmploc *volatile savehandler = exception_handler;
2241 struct jmploc jmploc;
2242
2243 SAVE_INT(saveint);
2244 if (setjmp(jmploc.loc))
2245 err = 1;
2246 else {
2247 exception_handler = &jmploc;
2248 setvar(name, val, flags);
2249 err = 0;
2250 }
2251 exception_handler = savehandler;
2252 RESTORE_INT(saveint);
2253 return err;
2254}
2255#endif
2256
2257/*
2258 * Unset the specified variable.
2259 */
2260static int
2261unsetvar(const char *s)
2262{
2263 struct var **vpp;
2264 struct var *vp;
2265 int retval;
2266
2267 vpp = findvar(hashvar(s), s);
2268 vp = *vpp;
2269 retval = 2;
2270 if (vp) {
2271 int flags = vp->flags;
2272
2273 retval = 1;
2274 if (flags & VREADONLY)
2275 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002276#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002277 vp->flags &= ~VDYNAMIC;
2278#endif
2279 if (flags & VUNSET)
2280 goto ok;
2281 if ((flags & VSTRFIXED) == 0) {
2282 INT_OFF;
2283 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002284 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285 *vpp = vp->next;
2286 free(vp);
2287 INT_ON;
2288 } else {
2289 setvar(s, 0, 0);
2290 vp->flags &= ~VEXPORT;
2291 }
2292 ok:
2293 retval = 0;
2294 }
2295 out:
2296 return retval;
2297}
2298
2299/*
2300 * Process a linked list of variable assignments.
2301 */
2302static void
2303listsetvar(struct strlist *list_set_var, int flags)
2304{
2305 struct strlist *lp = list_set_var;
2306
2307 if (!lp)
2308 return;
2309 INT_OFF;
2310 do {
2311 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002312 lp = lp->next;
2313 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002314 INT_ON;
2315}
2316
2317/*
2318 * Generate a list of variables satisfying the given conditions.
2319 */
2320static char **
2321listvars(int on, int off, char ***end)
2322{
2323 struct var **vpp;
2324 struct var *vp;
2325 char **ep;
2326 int mask;
2327
2328 STARTSTACKSTR(ep);
2329 vpp = vartab;
2330 mask = on | off;
2331 do {
2332 for (vp = *vpp; vp; vp = vp->next) {
2333 if ((vp->flags & mask) == on) {
2334 if (ep == stackstrend())
2335 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002336 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002337 }
2338 }
2339 } while (++vpp < vartab + VTABSIZE);
2340 if (ep == stackstrend())
2341 ep = growstackstr();
2342 if (end)
2343 *end = ep;
2344 *ep++ = NULL;
2345 return grabstackstr(ep);
2346}
2347
2348
2349/* ============ Path search helper
2350 *
2351 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002352 * of the path before the first call; path_advance will update
2353 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002354 * the possible path expansions in sequence. If an option (indicated by
2355 * a percent sign) appears in the path entry then the global variable
2356 * pathopt will be set to point to it; otherwise pathopt will be set to
2357 * NULL.
2358 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002359static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002360
2361static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002362path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002363{
2364 const char *p;
2365 char *q;
2366 const char *start;
2367 size_t len;
2368
2369 if (*path == NULL)
2370 return NULL;
2371 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002372 for (p = start; *p && *p != ':' && *p != '%'; p++)
2373 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002374 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2375 while (stackblocksize() < len)
2376 growstackblock();
2377 q = stackblock();
2378 if (p != start) {
2379 memcpy(q, start, p - start);
2380 q += p - start;
2381 *q++ = '/';
2382 }
2383 strcpy(q, name);
2384 pathopt = NULL;
2385 if (*p == '%') {
2386 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002387 while (*p && *p != ':')
2388 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002389 }
2390 if (*p == ':')
2391 *path = p + 1;
2392 else
2393 *path = NULL;
2394 return stalloc(len);
2395}
2396
2397
2398/* ============ Prompt */
2399
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002400static smallint doprompt; /* if set, prompt the user */
2401static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002402
2403#if ENABLE_FEATURE_EDITING
2404static line_input_t *line_input_state;
2405static const char *cmdedit_prompt;
2406static void
2407putprompt(const char *s)
2408{
2409 if (ENABLE_ASH_EXPAND_PRMT) {
2410 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002411 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412 return;
2413 }
2414 cmdedit_prompt = s;
2415}
2416#else
2417static void
2418putprompt(const char *s)
2419{
2420 out2str(s);
2421}
2422#endif
2423
2424#if ENABLE_ASH_EXPAND_PRMT
2425/* expandstr() needs parsing machinery, so it is far away ahead... */
2426static const char *expandstr(const char *ps);
2427#else
2428#define expandstr(s) s
2429#endif
2430
2431static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002432setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002433{
2434 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002435 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2436
2437 if (!do_set)
2438 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002439
2440 needprompt = 0;
2441
2442 switch (whichprompt) {
2443 case 1:
2444 prompt = ps1val();
2445 break;
2446 case 2:
2447 prompt = ps2val();
2448 break;
2449 default: /* 0 */
2450 prompt = nullstr;
2451 }
2452#if ENABLE_ASH_EXPAND_PRMT
2453 setstackmark(&smark);
2454 stalloc(stackblocksize());
2455#endif
2456 putprompt(expandstr(prompt));
2457#if ENABLE_ASH_EXPAND_PRMT
2458 popstackmark(&smark);
2459#endif
2460}
2461
2462
2463/* ============ The cd and pwd commands */
2464
2465#define CD_PHYSICAL 1
2466#define CD_PRINT 2
2467
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002468static int
2469cdopt(void)
2470{
2471 int flags = 0;
2472 int i, j;
2473
2474 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002475 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476 if (i != j) {
2477 flags ^= CD_PHYSICAL;
2478 j = i;
2479 }
2480 }
2481
2482 return flags;
2483}
2484
2485/*
2486 * Update curdir (the name of the current directory) in response to a
2487 * cd command.
2488 */
2489static const char *
2490updatepwd(const char *dir)
2491{
2492 char *new;
2493 char *p;
2494 char *cdcomppath;
2495 const char *lim;
2496
2497 cdcomppath = ststrdup(dir);
2498 STARTSTACKSTR(new);
2499 if (*dir != '/') {
2500 if (curdir == nullstr)
2501 return 0;
2502 new = stack_putstr(curdir, new);
2503 }
2504 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002505 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002506 if (*dir != '/') {
2507 if (new[-1] != '/')
2508 USTPUTC('/', new);
2509 if (new > lim && *lim == '/')
2510 lim++;
2511 } else {
2512 USTPUTC('/', new);
2513 cdcomppath++;
2514 if (dir[1] == '/' && dir[2] != '/') {
2515 USTPUTC('/', new);
2516 cdcomppath++;
2517 lim++;
2518 }
2519 }
2520 p = strtok(cdcomppath, "/");
2521 while (p) {
2522 switch (*p) {
2523 case '.':
2524 if (p[1] == '.' && p[2] == '\0') {
2525 while (new > lim) {
2526 STUNPUTC(new);
2527 if (new[-1] == '/')
2528 break;
2529 }
2530 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002531 }
2532 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002533 break;
2534 /* fall through */
2535 default:
2536 new = stack_putstr(p, new);
2537 USTPUTC('/', new);
2538 }
2539 p = strtok(0, "/");
2540 }
2541 if (new > lim)
2542 STUNPUTC(new);
2543 *new = 0;
2544 return stackblock();
2545}
2546
2547/*
2548 * Find out what the current directory is. If we already know the current
2549 * directory, this routine returns immediately.
2550 */
2551static char *
2552getpwd(void)
2553{
Denis Vlasenko01631112007-12-16 17:20:38 +00002554 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002555 return dir ? dir : nullstr;
2556}
2557
2558static void
2559setpwd(const char *val, int setold)
2560{
2561 char *oldcur, *dir;
2562
2563 oldcur = dir = curdir;
2564
2565 if (setold) {
2566 setvar("OLDPWD", oldcur, VEXPORT);
2567 }
2568 INT_OFF;
2569 if (physdir != nullstr) {
2570 if (physdir != oldcur)
2571 free(physdir);
2572 physdir = nullstr;
2573 }
2574 if (oldcur == val || !val) {
2575 char *s = getpwd();
2576 physdir = s;
2577 if (!val)
2578 dir = s;
2579 } else
2580 dir = ckstrdup(val);
2581 if (oldcur != dir && oldcur != nullstr) {
2582 free(oldcur);
2583 }
2584 curdir = dir;
2585 INT_ON;
2586 setvar("PWD", dir, VEXPORT);
2587}
2588
2589static void hashcd(void);
2590
2591/*
2592 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2593 * know that the current directory has changed.
2594 */
2595static int
2596docd(const char *dest, int flags)
2597{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002598 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002599 int err;
2600
2601 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2602
2603 INT_OFF;
2604 if (!(flags & CD_PHYSICAL)) {
2605 dir = updatepwd(dest);
2606 if (dir)
2607 dest = dir;
2608 }
2609 err = chdir(dest);
2610 if (err)
2611 goto out;
2612 setpwd(dir, 1);
2613 hashcd();
2614 out:
2615 INT_ON;
2616 return err;
2617}
2618
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002619static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002620cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002621{
2622 const char *dest;
2623 const char *path;
2624 const char *p;
2625 char c;
2626 struct stat statb;
2627 int flags;
2628
2629 flags = cdopt();
2630 dest = *argptr;
2631 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002632 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002633 else if (LONE_DASH(dest)) {
2634 dest = bltinlookup("OLDPWD");
2635 flags |= CD_PRINT;
2636 }
2637 if (!dest)
2638 dest = nullstr;
2639 if (*dest == '/')
2640 goto step7;
2641 if (*dest == '.') {
2642 c = dest[1];
2643 dotdot:
2644 switch (c) {
2645 case '\0':
2646 case '/':
2647 goto step6;
2648 case '.':
2649 c = dest[2];
2650 if (c != '.')
2651 goto dotdot;
2652 }
2653 }
2654 if (!*dest)
2655 dest = ".";
2656 path = bltinlookup("CDPATH");
2657 if (!path) {
2658 step6:
2659 step7:
2660 p = dest;
2661 goto docd;
2662 }
2663 do {
2664 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002665 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002666 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2667 if (c && c != ':')
2668 flags |= CD_PRINT;
2669 docd:
2670 if (!docd(p, flags))
2671 goto out;
2672 break;
2673 }
2674 } while (path);
2675 ash_msg_and_raise_error("can't cd to %s", dest);
2676 /* NOTREACHED */
2677 out:
2678 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002679 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002680 return 0;
2681}
2682
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002683static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002684pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002685{
2686 int flags;
2687 const char *dir = curdir;
2688
2689 flags = cdopt();
2690 if (flags) {
2691 if (physdir == nullstr)
2692 setpwd(dir, 0);
2693 dir = physdir;
2694 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002695 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002696 return 0;
2697}
2698
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002699
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002700/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002701
Denis Vlasenko834dee72008-10-07 09:18:30 +00002702
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002703#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002704
Eric Andersenc470f442003-07-28 09:56:35 +00002705/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002706#define CWORD 0 /* character is nothing special */
2707#define CNL 1 /* newline character */
2708#define CBACK 2 /* a backslash character */
2709#define CSQUOTE 3 /* single quote */
2710#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002711#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002712#define CBQUOTE 6 /* backwards single quote */
2713#define CVAR 7 /* a dollar sign */
2714#define CENDVAR 8 /* a '}' character */
2715#define CLP 9 /* a left paren in arithmetic */
2716#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002717#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002718#define CCTL 12 /* like CWORD, except it must be escaped */
2719#define CSPCL 13 /* these terminate a word */
2720#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002721
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002722#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002723#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002724# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002725#endif
2726
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002727#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002728
Mike Frysinger98c52642009-04-02 10:02:37 +00002729#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002730# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002731#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002732# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002733#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002734static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002735#if ENABLE_ASH_ALIAS
2736 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2737#endif
2738 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2739 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2740 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2741 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2742 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2743 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2744 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2745 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2746 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2747 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2748 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002749#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2751 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2752 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2753#endif
2754#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002755};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002756/* Constants below must match table above */
2757enum {
2758#if ENABLE_ASH_ALIAS
2759 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2760#endif
2761 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2762 CNL_CNL_CNL_CNL , /* 2 */
2763 CWORD_CCTL_CCTL_CWORD , /* 3 */
2764 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2765 CVAR_CVAR_CWORD_CVAR , /* 5 */
2766 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2767 CSPCL_CWORD_CWORD_CLP , /* 7 */
2768 CSPCL_CWORD_CWORD_CRP , /* 8 */
2769 CBACK_CBACK_CCTL_CBACK , /* 9 */
2770 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2771 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2772 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2773 CWORD_CWORD_CWORD_CWORD , /* 13 */
2774 CCTL_CCTL_CCTL_CCTL , /* 14 */
2775};
Eric Andersen2870d962001-07-02 17:27:21 +00002776
Denys Vlasenkocd716832009-11-28 22:14:02 +01002777/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2778 * caller must ensure proper cast on it if c is *char_ptr!
2779 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002780/* Values for syntax param */
2781#define BASESYNTAX 0 /* not in quotes */
2782#define DQSYNTAX 1 /* in double quotes */
2783#define SQSYNTAX 2 /* in single quotes */
2784#define ARISYNTAX 3 /* in arithmetic */
2785#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002786
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002787#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002788
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002789static int
2790SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002791{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002792 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002793# if ENABLE_ASH_ALIAS
2794 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002795 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2796 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2797 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2798 11, 3 /* "}~" */
2799 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002800# else
2801 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002802 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2803 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2804 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2805 10, 2 /* "}~" */
2806 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002807# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002808 const char *s;
2809 int indx;
2810
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002811 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002812 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002813# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002814 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002815 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002816 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002817# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002818 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002819 /* Cast is purely for paranoia here,
2820 * just in case someone passed signed char to us */
2821 if ((unsigned char)c >= CTL_FIRST
2822 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002823 ) {
2824 return CCTL;
2825 }
2826 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002828 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002829 indx = syntax_index_table[s - spec_symbls];
2830 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002831 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002832}
2833
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002834#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002835
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002836static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002837 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002838 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2839 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2840 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2841 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2842 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2843 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2844 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2845 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2846 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2848 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2849 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2871 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2872 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2873 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2874 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2875 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2876 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2877 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2878 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2879 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2880 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2881 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2883 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2884 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2885 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2886 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2887 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2888 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2889 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2897 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2898 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2899 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2900 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2901 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2930 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2931 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2932 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2935 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2963 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2964 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2965 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2966 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2967 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2968 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2969 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2970 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2971 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2972 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2973 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2974 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2975 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2976 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2977 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2978 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2979 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2980 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2981 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2982 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2983 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 153 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 154 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 155 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 156 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 157 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 158 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 159 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 160 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003094 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003095# if ENABLE_ASH_ALIAS
3096 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3097# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003098};
3099
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003100# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003101
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003102#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003103
Eric Andersen2870d962001-07-02 17:27:21 +00003104
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003105/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003106
Denis Vlasenko131ae172007-02-18 13:00:19 +00003107#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003108
3109#define ALIASINUSE 1
3110#define ALIASDEAD 2
3111
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003112struct alias {
3113 struct alias *next;
3114 char *name;
3115 char *val;
3116 int flag;
3117};
3118
Denis Vlasenko01631112007-12-16 17:20:38 +00003119
3120static struct alias **atab; // [ATABSIZE];
3121#define INIT_G_alias() do { \
3122 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3123} while (0)
3124
Eric Andersen2870d962001-07-02 17:27:21 +00003125
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003126static struct alias **
3127__lookupalias(const char *name) {
3128 unsigned int hashval;
3129 struct alias **app;
3130 const char *p;
3131 unsigned int ch;
3132
3133 p = name;
3134
3135 ch = (unsigned char)*p;
3136 hashval = ch << 4;
3137 while (ch) {
3138 hashval += ch;
3139 ch = (unsigned char)*++p;
3140 }
3141 app = &atab[hashval % ATABSIZE];
3142
3143 for (; *app; app = &(*app)->next) {
3144 if (strcmp(name, (*app)->name) == 0) {
3145 break;
3146 }
3147 }
3148
3149 return app;
3150}
3151
3152static struct alias *
3153lookupalias(const char *name, int check)
3154{
3155 struct alias *ap = *__lookupalias(name);
3156
3157 if (check && ap && (ap->flag & ALIASINUSE))
3158 return NULL;
3159 return ap;
3160}
3161
3162static struct alias *
3163freealias(struct alias *ap)
3164{
3165 struct alias *next;
3166
3167 if (ap->flag & ALIASINUSE) {
3168 ap->flag |= ALIASDEAD;
3169 return ap;
3170 }
3171
3172 next = ap->next;
3173 free(ap->name);
3174 free(ap->val);
3175 free(ap);
3176 return next;
3177}
Eric Andersencb57d552001-06-28 07:25:16 +00003178
Eric Andersenc470f442003-07-28 09:56:35 +00003179static void
3180setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003181{
3182 struct alias *ap, **app;
3183
3184 app = __lookupalias(name);
3185 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003186 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003187 if (ap) {
3188 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003189 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003190 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003191 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003192 ap->flag &= ~ALIASDEAD;
3193 } else {
3194 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003195 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003196 ap->name = ckstrdup(name);
3197 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003198 /*ap->flag = 0; - ckzalloc did it */
3199 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003200 *app = ap;
3201 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003202 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003203}
3204
Eric Andersenc470f442003-07-28 09:56:35 +00003205static int
3206unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003207{
Eric Andersencb57d552001-06-28 07:25:16 +00003208 struct alias **app;
3209
3210 app = __lookupalias(name);
3211
3212 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003213 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003214 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003215 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003216 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003217 }
3218
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003219 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003220}
3221
Eric Andersenc470f442003-07-28 09:56:35 +00003222static void
3223rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003224{
Eric Andersencb57d552001-06-28 07:25:16 +00003225 struct alias *ap, **app;
3226 int i;
3227
Denis Vlasenkob012b102007-02-19 22:43:01 +00003228 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003229 for (i = 0; i < ATABSIZE; i++) {
3230 app = &atab[i];
3231 for (ap = *app; ap; ap = *app) {
3232 *app = freealias(*app);
3233 if (ap == *app) {
3234 app = &ap->next;
3235 }
3236 }
3237 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003238 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003239}
3240
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003241static void
3242printalias(const struct alias *ap)
3243{
3244 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3245}
3246
Eric Andersencb57d552001-06-28 07:25:16 +00003247/*
3248 * TODO - sort output
3249 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003250static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003251aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003252{
3253 char *n, *v;
3254 int ret = 0;
3255 struct alias *ap;
3256
Denis Vlasenko68404f12008-03-17 09:00:54 +00003257 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003258 int i;
3259
Denis Vlasenko68404f12008-03-17 09:00:54 +00003260 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003261 for (ap = atab[i]; ap; ap = ap->next) {
3262 printalias(ap);
3263 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003264 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003265 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003266 }
3267 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003268 v = strchr(n+1, '=');
3269 if (v == NULL) { /* n+1: funny ksh stuff */
3270 ap = *__lookupalias(n);
3271 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003272 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003273 ret = 1;
3274 } else
3275 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003276 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003277 *v++ = '\0';
3278 setalias(n, v);
3279 }
3280 }
3281
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003282 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003283}
3284
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003285static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003286unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003287{
3288 int i;
3289
3290 while ((i = nextopt("a")) != '\0') {
3291 if (i == 'a') {
3292 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003293 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003294 }
3295 }
3296 for (i = 0; *argptr; argptr++) {
3297 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003298 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003299 i = 1;
3300 }
3301 }
3302
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003303 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003304}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003305
Denis Vlasenko131ae172007-02-18 13:00:19 +00003306#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003307
Eric Andersenc470f442003-07-28 09:56:35 +00003308
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003309/* ============ jobs.c */
3310
3311/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003312#define FORK_FG 0
3313#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003314#define FORK_NOJOB 2
3315
3316/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003317#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3318#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3319#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003320
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003321/*
3322 * A job structure contains information about a job. A job is either a
3323 * single process or a set of processes contained in a pipeline. In the
3324 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3325 * array of pids.
3326 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003328 pid_t ps_pid; /* process id */
3329 int ps_status; /* last process status from wait() */
3330 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331};
3332
3333struct job {
3334 struct procstat ps0; /* status of process */
3335 struct procstat *ps; /* status or processes when more than one */
3336#if JOBS
3337 int stopstatus; /* status of a stopped job */
3338#endif
3339 uint32_t
3340 nprocs: 16, /* number of processes */
3341 state: 8,
3342#define JOBRUNNING 0 /* at least one proc running */
3343#define JOBSTOPPED 1 /* all procs are stopped */
3344#define JOBDONE 2 /* all procs are completed */
3345#if JOBS
3346 sigint: 1, /* job was killed by SIGINT */
3347 jobctl: 1, /* job running under job control */
3348#endif
3349 waited: 1, /* true if this entry has been waited for */
3350 used: 1, /* true if this entry is in used */
3351 changed: 1; /* true if status has changed */
3352 struct job *prev_job; /* previous job */
3353};
3354
Denis Vlasenko68404f12008-03-17 09:00:54 +00003355static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003356static int forkshell(struct job *, union node *, int);
3357static int waitforjob(struct job *);
3358
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003359#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003360enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003361#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003362#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003363static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003365#endif
3366
3367/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003368 * Ignore a signal.
3369 */
3370static void
3371ignoresig(int signo)
3372{
3373 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3374 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3375 /* No, need to do it */
3376 signal(signo, SIG_IGN);
3377 }
3378 sigmode[signo - 1] = S_HARD_IGN;
3379}
3380
3381/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003382 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003383 */
3384static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003385signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003386{
3387 gotsig[signo - 1] = 1;
3388
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003389 if (signo == SIGINT && !trap[SIGINT]) {
3390 if (!suppress_int) {
3391 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003392 raise_interrupt(); /* does not return */
3393 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003394 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003395 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003396 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003397 }
3398}
3399
3400/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003401 * Set the signal handler for the specified signal. The routine figures
3402 * out what it should be set to.
3403 */
3404static void
3405setsignal(int signo)
3406{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003407 char *t;
3408 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003409 struct sigaction act;
3410
3411 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003412 new_act = S_DFL;
3413 if (t != NULL) { /* trap for this sig is set */
3414 new_act = S_CATCH;
3415 if (t[0] == '\0') /* trap is "": ignore this sig */
3416 new_act = S_IGN;
3417 }
3418
3419 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003420 switch (signo) {
3421 case SIGINT:
3422 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003423 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003424 break;
3425 case SIGQUIT:
3426#if DEBUG
3427 if (debug)
3428 break;
3429#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003430 /* man bash:
3431 * "In all cases, bash ignores SIGQUIT. Non-builtin
3432 * commands run by bash have signal handlers
3433 * set to the values inherited by the shell
3434 * from its parent". */
3435 new_act = S_IGN;
3436 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437 case SIGTERM:
3438 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003439 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003440 break;
3441#if JOBS
3442 case SIGTSTP:
3443 case SIGTTOU:
3444 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003445 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003446 break;
3447#endif
3448 }
3449 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003450//TODO: if !rootshell, we reset SIGQUIT to DFL,
3451//whereas we have to restore it to what shell got on entry
3452//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003453
3454 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003455 cur_act = *t;
3456 if (cur_act == 0) {
3457 /* current setting is not yet known */
3458 if (sigaction(signo, NULL, &act)) {
3459 /* pretend it worked; maybe we should give a warning,
3460 * but other shells don't. We don't alter sigmode,
3461 * so we retry every time.
3462 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003463 return;
3464 }
3465 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003466 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003467 if (mflag
3468 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3469 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003470 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003471 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003472 }
3473 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003474 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003475 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003476
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003477 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003478 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003479 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003480 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003481 break;
3482 case S_IGN:
3483 act.sa_handler = SIG_IGN;
3484 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003485 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003486
3487 /* flags and mask matter only if !DFL and !IGN, but we do it
3488 * for all cases for more deterministic behavior:
3489 */
3490 act.sa_flags = 0;
3491 sigfillset(&act.sa_mask);
3492
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003493 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003494
3495 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003496}
3497
3498/* mode flags for set_curjob */
3499#define CUR_DELETE 2
3500#define CUR_RUNNING 1
3501#define CUR_STOPPED 0
3502
3503/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003504#define DOWAIT_NONBLOCK WNOHANG
3505#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003506
3507#if JOBS
3508/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003509static int initialpgrp; //references:2
3510static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003511#endif
3512/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003513static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003514/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003515static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003516/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003517static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003518/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003519static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003520
3521static void
3522set_curjob(struct job *jp, unsigned mode)
3523{
3524 struct job *jp1;
3525 struct job **jpp, **curp;
3526
3527 /* first remove from list */
3528 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003529 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003530 jp1 = *jpp;
3531 if (jp1 == jp)
3532 break;
3533 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003534 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003535 *jpp = jp1->prev_job;
3536
3537 /* Then re-insert in correct position */
3538 jpp = curp;
3539 switch (mode) {
3540 default:
3541#if DEBUG
3542 abort();
3543#endif
3544 case CUR_DELETE:
3545 /* job being deleted */
3546 break;
3547 case CUR_RUNNING:
3548 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003549 * put after all stopped jobs.
3550 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003551 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003552 jp1 = *jpp;
3553#if JOBS
3554 if (!jp1 || jp1->state != JOBSTOPPED)
3555#endif
3556 break;
3557 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003558 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003559 /* FALLTHROUGH */
3560#if JOBS
3561 case CUR_STOPPED:
3562#endif
3563 /* newly stopped job - becomes curjob */
3564 jp->prev_job = *jpp;
3565 *jpp = jp;
3566 break;
3567 }
3568}
3569
3570#if JOBS || DEBUG
3571static int
3572jobno(const struct job *jp)
3573{
3574 return jp - jobtab + 1;
3575}
3576#endif
3577
3578/*
3579 * Convert a job name to a job structure.
3580 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003581#if !JOBS
3582#define getjob(name, getctl) getjob(name)
3583#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584static struct job *
3585getjob(const char *name, int getctl)
3586{
3587 struct job *jp;
3588 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003589 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003590 unsigned num;
3591 int c;
3592 const char *p;
3593 char *(*match)(const char *, const char *);
3594
3595 jp = curjob;
3596 p = name;
3597 if (!p)
3598 goto currentjob;
3599
3600 if (*p != '%')
3601 goto err;
3602
3603 c = *++p;
3604 if (!c)
3605 goto currentjob;
3606
3607 if (!p[1]) {
3608 if (c == '+' || c == '%') {
3609 currentjob:
3610 err_msg = "No current job";
3611 goto check;
3612 }
3613 if (c == '-') {
3614 if (jp)
3615 jp = jp->prev_job;
3616 err_msg = "No previous job";
3617 check:
3618 if (!jp)
3619 goto err;
3620 goto gotit;
3621 }
3622 }
3623
3624 if (is_number(p)) {
3625 num = atoi(p);
3626 if (num < njobs) {
3627 jp = jobtab + num - 1;
3628 if (jp->used)
3629 goto gotit;
3630 goto err;
3631 }
3632 }
3633
3634 match = prefix;
3635 if (*p == '?') {
3636 match = strstr;
3637 p++;
3638 }
3639
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003640 found = NULL;
3641 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003642 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003643 if (found)
3644 goto err;
3645 found = jp;
3646 err_msg = "%s: ambiguous";
3647 }
3648 jp = jp->prev_job;
3649 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003650 if (!found)
3651 goto err;
3652 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003653
3654 gotit:
3655#if JOBS
3656 err_msg = "job %s not created under job control";
3657 if (getctl && jp->jobctl == 0)
3658 goto err;
3659#endif
3660 return jp;
3661 err:
3662 ash_msg_and_raise_error(err_msg, name);
3663}
3664
3665/*
3666 * Mark a job structure as unused.
3667 */
3668static void
3669freejob(struct job *jp)
3670{
3671 struct procstat *ps;
3672 int i;
3673
3674 INT_OFF;
3675 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003676 if (ps->ps_cmd != nullstr)
3677 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678 }
3679 if (jp->ps != &jp->ps0)
3680 free(jp->ps);
3681 jp->used = 0;
3682 set_curjob(jp, CUR_DELETE);
3683 INT_ON;
3684}
3685
3686#if JOBS
3687static void
3688xtcsetpgrp(int fd, pid_t pgrp)
3689{
3690 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003691 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003692}
3693
3694/*
3695 * Turn job control on and off.
3696 *
3697 * Note: This code assumes that the third arg to ioctl is a character
3698 * pointer, which is true on Berkeley systems but not System V. Since
3699 * System V doesn't have job control yet, this isn't a problem now.
3700 *
3701 * Called with interrupts off.
3702 */
3703static void
3704setjobctl(int on)
3705{
3706 int fd;
3707 int pgrp;
3708
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003709 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003710 return;
3711 if (on) {
3712 int ofd;
3713 ofd = fd = open(_PATH_TTY, O_RDWR);
3714 if (fd < 0) {
3715 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3716 * That sometimes helps to acquire controlling tty.
3717 * Obviously, a workaround for bugs when someone
3718 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003719 fd = 2;
3720 while (!isatty(fd))
3721 if (--fd < 0)
3722 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003723 }
3724 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003725 if (ofd >= 0)
3726 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003727 if (fd < 0)
3728 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003729 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003730 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003731 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003732 pgrp = tcgetpgrp(fd);
3733 if (pgrp < 0) {
3734 out:
3735 ash_msg("can't access tty; job control turned off");
3736 mflag = on = 0;
3737 goto close;
3738 }
3739 if (pgrp == getpgrp())
3740 break;
3741 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003742 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003743 initialpgrp = pgrp;
3744
3745 setsignal(SIGTSTP);
3746 setsignal(SIGTTOU);
3747 setsignal(SIGTTIN);
3748 pgrp = rootpid;
3749 setpgid(0, pgrp);
3750 xtcsetpgrp(fd, pgrp);
3751 } else {
3752 /* turning job control off */
3753 fd = ttyfd;
3754 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003755 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003756 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003757 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003758 setpgid(0, pgrp);
3759 setsignal(SIGTSTP);
3760 setsignal(SIGTTOU);
3761 setsignal(SIGTTIN);
3762 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003763 if (fd >= 0)
3764 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765 fd = -1;
3766 }
3767 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003768 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003769}
3770
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003771static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772killcmd(int argc, char **argv)
3773{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003774 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003775 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003776 do {
3777 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003778 /*
3779 * "kill %N" - job kill
3780 * Converting to pgrp / pid kill
3781 */
3782 struct job *jp;
3783 char *dst;
3784 int j, n;
3785
3786 jp = getjob(argv[i], 0);
3787 /*
3788 * In jobs started under job control, we signal
3789 * entire process group by kill -PGRP_ID.
3790 * This happens, f.e., in interactive shell.
3791 *
3792 * Otherwise, we signal each child via
3793 * kill PID1 PID2 PID3.
3794 * Testcases:
3795 * sh -c 'sleep 1|sleep 1 & kill %1'
3796 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3797 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3798 */
3799 n = jp->nprocs; /* can't be 0 (I hope) */
3800 if (jp->jobctl)
3801 n = 1;
3802 dst = alloca(n * sizeof(int)*4);
3803 argv[i] = dst;
3804 for (j = 0; j < n; j++) {
3805 struct procstat *ps = &jp->ps[j];
3806 /* Skip non-running and not-stopped members
3807 * (i.e. dead members) of the job
3808 */
3809 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3810 continue;
3811 /*
3812 * kill_main has matching code to expect
3813 * leading space. Needed to not confuse
3814 * negative pids with "kill -SIGNAL_NO" syntax
3815 */
3816 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3817 }
3818 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003819 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003820 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003822 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003823}
3824
3825static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003826showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003827{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003828 struct procstat *ps;
3829 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003830
Denys Vlasenko285ad152009-12-04 23:02:27 +01003831 psend = jp->ps + jp->nprocs;
3832 for (ps = jp->ps + 1; ps < psend; ps++)
3833 printf(" | %s", ps->ps_cmd);
3834 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835 flush_stdout_stderr();
3836}
3837
3838
3839static int
3840restartjob(struct job *jp, int mode)
3841{
3842 struct procstat *ps;
3843 int i;
3844 int status;
3845 pid_t pgid;
3846
3847 INT_OFF;
3848 if (jp->state == JOBDONE)
3849 goto out;
3850 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003851 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003852 if (mode == FORK_FG)
3853 xtcsetpgrp(ttyfd, pgid);
3854 killpg(pgid, SIGCONT);
3855 ps = jp->ps;
3856 i = jp->nprocs;
3857 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003858 if (WIFSTOPPED(ps->ps_status)) {
3859 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003860 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003861 ps++;
3862 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003863 out:
3864 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3865 INT_ON;
3866 return status;
3867}
3868
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003869static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003870fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003871{
3872 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003873 int mode;
3874 int retval;
3875
3876 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3877 nextopt(nullstr);
3878 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003879 do {
3880 jp = getjob(*argv, 1);
3881 if (mode == FORK_BG) {
3882 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003883 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003884 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003885 out1str(jp->ps[0].ps_cmd);
3886 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003887 retval = restartjob(jp, mode);
3888 } while (*argv && *++argv);
3889 return retval;
3890}
3891#endif
3892
3893static int
3894sprint_status(char *s, int status, int sigonly)
3895{
3896 int col;
3897 int st;
3898
3899 col = 0;
3900 if (!WIFEXITED(status)) {
3901#if JOBS
3902 if (WIFSTOPPED(status))
3903 st = WSTOPSIG(status);
3904 else
3905#endif
3906 st = WTERMSIG(status);
3907 if (sigonly) {
3908 if (st == SIGINT || st == SIGPIPE)
3909 goto out;
3910#if JOBS
3911 if (WIFSTOPPED(status))
3912 goto out;
3913#endif
3914 }
3915 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003916//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003917 col = fmtstr(s, 32, strsignal(st));
3918 if (WCOREDUMP(status)) {
3919 col += fmtstr(s + col, 16, " (core dumped)");
3920 }
3921 } else if (!sigonly) {
3922 st = WEXITSTATUS(status);
3923 if (st)
3924 col = fmtstr(s, 16, "Done(%d)", st);
3925 else
3926 col = fmtstr(s, 16, "Done");
3927 }
3928 out:
3929 return col;
3930}
3931
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003933dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003934{
3935 int pid;
3936 int status;
3937 struct job *jp;
3938 struct job *thisjob;
3939 int state;
3940
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003941 TRACE(("dowait(0x%x) called\n", wait_flags));
3942
3943 /* Do a wait system call. If job control is compiled in, we accept
3944 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3945 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003946 if (doing_jobctl)
3947 wait_flags |= WUNTRACED;
3948 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003949 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3950 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003951 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003952 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003953
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003954 INT_OFF;
3955 thisjob = NULL;
3956 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003957 struct procstat *ps;
3958 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003959 if (jp->state == JOBDONE)
3960 continue;
3961 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003962 ps = jp->ps;
3963 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003964 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003965 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003966 TRACE(("Job %d: changing status of proc %d "
3967 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003968 jobno(jp), pid, ps->ps_status, status));
3969 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 thisjob = jp;
3971 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003972 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003973 state = JOBRUNNING;
3974#if JOBS
3975 if (state == JOBRUNNING)
3976 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003977 if (WIFSTOPPED(ps->ps_status)) {
3978 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003979 state = JOBSTOPPED;
3980 }
3981#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003982 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 if (thisjob)
3984 goto gotjob;
3985 }
3986#if JOBS
3987 if (!WIFSTOPPED(status))
3988#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003989 jobless--;
3990 goto out;
3991
3992 gotjob:
3993 if (state != JOBRUNNING) {
3994 thisjob->changed = 1;
3995
3996 if (thisjob->state != state) {
3997 TRACE(("Job %d: changing state from %d to %d\n",
3998 jobno(thisjob), thisjob->state, state));
3999 thisjob->state = state;
4000#if JOBS
4001 if (state == JOBSTOPPED) {
4002 set_curjob(thisjob, CUR_STOPPED);
4003 }
4004#endif
4005 }
4006 }
4007
4008 out:
4009 INT_ON;
4010
4011 if (thisjob && thisjob == job) {
4012 char s[48 + 1];
4013 int len;
4014
4015 len = sprint_status(s, status, 1);
4016 if (len) {
4017 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004018 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004019 out2str(s);
4020 }
4021 }
4022 return pid;
4023}
4024
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004025static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004026blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004027{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004028 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004029 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004030 raise_exception(EXSIG);
4031 return pid;
4032}
4033
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004034#if JOBS
4035static void
4036showjob(FILE *out, struct job *jp, int mode)
4037{
4038 struct procstat *ps;
4039 struct procstat *psend;
4040 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004041 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004042 char s[80];
4043
4044 ps = jp->ps;
4045
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004046 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004047 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004048 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004049 return;
4050 }
4051
4052 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004053 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054
4055 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004056 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004057 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004058 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004060 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004061 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004062
4063 psend = ps + jp->nprocs;
4064
4065 if (jp->state == JOBRUNNING) {
4066 strcpy(s + col, "Running");
4067 col += sizeof("Running") - 1;
4068 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004069 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070 if (jp->state == JOBSTOPPED)
4071 status = jp->stopstatus;
4072 col += sprint_status(s + col, status, 0);
4073 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004074 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004076 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4077 * or prints several "PID | <cmdN>" lines,
4078 * depending on SHOW_PIDS bit.
4079 * We do not print status of individual processes
4080 * between PID and <cmdN>. bash does it, but not very well:
4081 * first line shows overall job status, not process status,
4082 * making it impossible to know 1st process status.
4083 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004084 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004085 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004087 s[0] = '\0';
4088 col = 33;
4089 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004090 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004092 fprintf(out, "%s%*c%s%s",
4093 s,
4094 33 - col >= 0 ? 33 - col : 0, ' ',
4095 ps == jp->ps ? "" : "| ",
4096 ps->ps_cmd
4097 );
4098 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004099 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004100
4101 jp->changed = 0;
4102
4103 if (jp->state == JOBDONE) {
4104 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4105 freejob(jp);
4106 }
4107}
4108
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004109/*
4110 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4111 * statuses have changed since the last call to showjobs.
4112 */
4113static void
4114showjobs(FILE *out, int mode)
4115{
4116 struct job *jp;
4117
Denys Vlasenko883cea42009-07-11 15:31:59 +02004118 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004119
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004120 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004121 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004122 continue;
4123
4124 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004125 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004126 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004127 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004128 }
4129}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004130
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004131static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004132jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004133{
4134 int mode, m;
4135
4136 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004137 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004138 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004139 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004140 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004141 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004142 }
4143
4144 argv = argptr;
4145 if (*argv) {
4146 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004147 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004148 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004149 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004150 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004151 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004152
4153 return 0;
4154}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004155#endif /* JOBS */
4156
Michael Abbott359da5e2009-12-04 23:03:29 +01004157/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004158static int
4159getstatus(struct job *job)
4160{
4161 int status;
4162 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004163 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004164
Michael Abbott359da5e2009-12-04 23:03:29 +01004165 /* Fetch last member's status */
4166 ps = job->ps + job->nprocs - 1;
4167 status = ps->ps_status;
4168 if (pipefail) {
4169 /* "set -o pipefail" mode: use last _nonzero_ status */
4170 while (status == 0 && --ps >= job->ps)
4171 status = ps->ps_status;
4172 }
4173
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174 retval = WEXITSTATUS(status);
4175 if (!WIFEXITED(status)) {
4176#if JOBS
4177 retval = WSTOPSIG(status);
4178 if (!WIFSTOPPED(status))
4179#endif
4180 {
4181 /* XXX: limits number of signals */
4182 retval = WTERMSIG(status);
4183#if JOBS
4184 if (retval == SIGINT)
4185 job->sigint = 1;
4186#endif
4187 }
4188 retval += 128;
4189 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004190 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004191 jobno(job), job->nprocs, status, retval));
4192 return retval;
4193}
4194
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004195static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004196waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004197{
4198 struct job *job;
4199 int retval;
4200 struct job *jp;
4201
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004202 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004203 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204
4205 nextopt(nullstr);
4206 retval = 0;
4207
4208 argv = argptr;
4209 if (!*argv) {
4210 /* wait for all jobs */
4211 for (;;) {
4212 jp = curjob;
4213 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004214 if (!jp) /* no running procs */
4215 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216 if (jp->state == JOBRUNNING)
4217 break;
4218 jp->waited = 1;
4219 jp = jp->prev_job;
4220 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004221 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004222 /* man bash:
4223 * "When bash is waiting for an asynchronous command via
4224 * the wait builtin, the reception of a signal for which a trap
4225 * has been set will cause the wait builtin to return immediately
4226 * with an exit status greater than 128, immediately after which
4227 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004228 *
4229 * blocking_wait_with_raise_on_sig raises signal handlers
4230 * if it gets no pid (pid < 0). However,
4231 * if child sends us a signal *and immediately exits*,
4232 * blocking_wait_with_raise_on_sig gets pid > 0
4233 * and does not handle pending_sig. Check this case: */
4234 if (pending_sig)
4235 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004236 }
4237 }
4238
4239 retval = 127;
4240 do {
4241 if (**argv != '%') {
4242 pid_t pid = number(*argv);
4243 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004244 while (1) {
4245 if (!job)
4246 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004247 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 break;
4249 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004250 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004251 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004252 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004253 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004254 /* loop until process terminated or stopped */
4255 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004256 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004257 job->waited = 1;
4258 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004259 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004260 } while (*++argv);
4261
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004262 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004263 return retval;
4264}
4265
4266static struct job *
4267growjobtab(void)
4268{
4269 size_t len;
4270 ptrdiff_t offset;
4271 struct job *jp, *jq;
4272
4273 len = njobs * sizeof(*jp);
4274 jq = jobtab;
4275 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4276
4277 offset = (char *)jp - (char *)jq;
4278 if (offset) {
4279 /* Relocate pointers */
4280 size_t l = len;
4281
4282 jq = (struct job *)((char *)jq + l);
4283 while (l) {
4284 l -= sizeof(*jp);
4285 jq--;
4286#define joff(p) ((struct job *)((char *)(p) + l))
4287#define jmove(p) (p) = (void *)((char *)(p) + offset)
4288 if (joff(jp)->ps == &jq->ps0)
4289 jmove(joff(jp)->ps);
4290 if (joff(jp)->prev_job)
4291 jmove(joff(jp)->prev_job);
4292 }
4293 if (curjob)
4294 jmove(curjob);
4295#undef joff
4296#undef jmove
4297 }
4298
4299 njobs += 4;
4300 jobtab = jp;
4301 jp = (struct job *)((char *)jp + len);
4302 jq = jp + 3;
4303 do {
4304 jq->used = 0;
4305 } while (--jq >= jp);
4306 return jp;
4307}
4308
4309/*
4310 * Return a new job structure.
4311 * Called with interrupts off.
4312 */
4313static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004314makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004315{
4316 int i;
4317 struct job *jp;
4318
4319 for (i = njobs, jp = jobtab; ; jp++) {
4320 if (--i < 0) {
4321 jp = growjobtab();
4322 break;
4323 }
4324 if (jp->used == 0)
4325 break;
4326 if (jp->state != JOBDONE || !jp->waited)
4327 continue;
4328#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004329 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004330 continue;
4331#endif
4332 freejob(jp);
4333 break;
4334 }
4335 memset(jp, 0, sizeof(*jp));
4336#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004337 /* jp->jobctl is a bitfield.
4338 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004339 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004340 jp->jobctl = 1;
4341#endif
4342 jp->prev_job = curjob;
4343 curjob = jp;
4344 jp->used = 1;
4345 jp->ps = &jp->ps0;
4346 if (nprocs > 1) {
4347 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4348 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004349 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004350 jobno(jp)));
4351 return jp;
4352}
4353
4354#if JOBS
4355/*
4356 * Return a string identifying a command (to be printed by the
4357 * jobs command).
4358 */
4359static char *cmdnextc;
4360
4361static void
4362cmdputs(const char *s)
4363{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004364 static const char vstype[VSTYPE + 1][3] = {
4365 "", "}", "-", "+", "?", "=",
4366 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004367 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004368 };
4369
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004370 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004371 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004372 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004373 unsigned char c;
4374 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004375 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004376
Denys Vlasenko46a14772009-12-10 21:27:13 +01004377 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004378 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4379 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004380 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004381 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382 switch (c) {
4383 case CTLESC:
4384 c = *p++;
4385 break;
4386 case CTLVAR:
4387 subtype = *p++;
4388 if ((subtype & VSTYPE) == VSLENGTH)
4389 str = "${#";
4390 else
4391 str = "${";
4392 if (!(subtype & VSQUOTE) == !(quoted & 1))
4393 goto dostr;
4394 quoted ^= 1;
4395 c = '"';
4396 break;
4397 case CTLENDVAR:
4398 str = "\"}" + !(quoted & 1);
4399 quoted >>= 1;
4400 subtype = 0;
4401 goto dostr;
4402 case CTLBACKQ:
4403 str = "$(...)";
4404 goto dostr;
4405 case CTLBACKQ+CTLQUOTE:
4406 str = "\"$(...)\"";
4407 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004408#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004409 case CTLARI:
4410 str = "$((";
4411 goto dostr;
4412 case CTLENDARI:
4413 str = "))";
4414 goto dostr;
4415#endif
4416 case CTLQUOTEMARK:
4417 quoted ^= 1;
4418 c = '"';
4419 break;
4420 case '=':
4421 if (subtype == 0)
4422 break;
4423 if ((subtype & VSTYPE) != VSNORMAL)
4424 quoted <<= 1;
4425 str = vstype[subtype & VSTYPE];
4426 if (subtype & VSNUL)
4427 c = ':';
4428 else
4429 goto checkstr;
4430 break;
4431 case '\'':
4432 case '\\':
4433 case '"':
4434 case '$':
4435 /* These can only happen inside quotes */
4436 cc[0] = c;
4437 str = cc;
4438 c = '\\';
4439 break;
4440 default:
4441 break;
4442 }
4443 USTPUTC(c, nextc);
4444 checkstr:
4445 if (!str)
4446 continue;
4447 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004448 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004449 USTPUTC(c, nextc);
4450 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004451 } /* while *p++ not NUL */
4452
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004453 if (quoted & 1) {
4454 USTPUTC('"', nextc);
4455 }
4456 *nextc = 0;
4457 cmdnextc = nextc;
4458}
4459
4460/* cmdtxt() and cmdlist() call each other */
4461static void cmdtxt(union node *n);
4462
4463static void
4464cmdlist(union node *np, int sep)
4465{
4466 for (; np; np = np->narg.next) {
4467 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004468 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469 cmdtxt(np);
4470 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004471 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004472 }
4473}
4474
4475static void
4476cmdtxt(union node *n)
4477{
4478 union node *np;
4479 struct nodelist *lp;
4480 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004481
4482 if (!n)
4483 return;
4484 switch (n->type) {
4485 default:
4486#if DEBUG
4487 abort();
4488#endif
4489 case NPIPE:
4490 lp = n->npipe.cmdlist;
4491 for (;;) {
4492 cmdtxt(lp->n);
4493 lp = lp->next;
4494 if (!lp)
4495 break;
4496 cmdputs(" | ");
4497 }
4498 break;
4499 case NSEMI:
4500 p = "; ";
4501 goto binop;
4502 case NAND:
4503 p = " && ";
4504 goto binop;
4505 case NOR:
4506 p = " || ";
4507 binop:
4508 cmdtxt(n->nbinary.ch1);
4509 cmdputs(p);
4510 n = n->nbinary.ch2;
4511 goto donode;
4512 case NREDIR:
4513 case NBACKGND:
4514 n = n->nredir.n;
4515 goto donode;
4516 case NNOT:
4517 cmdputs("!");
4518 n = n->nnot.com;
4519 donode:
4520 cmdtxt(n);
4521 break;
4522 case NIF:
4523 cmdputs("if ");
4524 cmdtxt(n->nif.test);
4525 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004527 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004528 cmdputs("; else ");
4529 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004530 } else {
4531 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004532 }
4533 p = "; fi";
4534 goto dotail;
4535 case NSUBSHELL:
4536 cmdputs("(");
4537 n = n->nredir.n;
4538 p = ")";
4539 goto dotail;
4540 case NWHILE:
4541 p = "while ";
4542 goto until;
4543 case NUNTIL:
4544 p = "until ";
4545 until:
4546 cmdputs(p);
4547 cmdtxt(n->nbinary.ch1);
4548 n = n->nbinary.ch2;
4549 p = "; done";
4550 dodo:
4551 cmdputs("; do ");
4552 dotail:
4553 cmdtxt(n);
4554 goto dotail2;
4555 case NFOR:
4556 cmdputs("for ");
4557 cmdputs(n->nfor.var);
4558 cmdputs(" in ");
4559 cmdlist(n->nfor.args, 1);
4560 n = n->nfor.body;
4561 p = "; done";
4562 goto dodo;
4563 case NDEFUN:
4564 cmdputs(n->narg.text);
4565 p = "() { ... }";
4566 goto dotail2;
4567 case NCMD:
4568 cmdlist(n->ncmd.args, 1);
4569 cmdlist(n->ncmd.redirect, 0);
4570 break;
4571 case NARG:
4572 p = n->narg.text;
4573 dotail2:
4574 cmdputs(p);
4575 break;
4576 case NHERE:
4577 case NXHERE:
4578 p = "<<...";
4579 goto dotail2;
4580 case NCASE:
4581 cmdputs("case ");
4582 cmdputs(n->ncase.expr->narg.text);
4583 cmdputs(" in ");
4584 for (np = n->ncase.cases; np; np = np->nclist.next) {
4585 cmdtxt(np->nclist.pattern);
4586 cmdputs(") ");
4587 cmdtxt(np->nclist.body);
4588 cmdputs(";; ");
4589 }
4590 p = "esac";
4591 goto dotail2;
4592 case NTO:
4593 p = ">";
4594 goto redir;
4595 case NCLOBBER:
4596 p = ">|";
4597 goto redir;
4598 case NAPPEND:
4599 p = ">>";
4600 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004601#if ENABLE_ASH_BASH_COMPAT
4602 case NTO2:
4603#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004604 case NTOFD:
4605 p = ">&";
4606 goto redir;
4607 case NFROM:
4608 p = "<";
4609 goto redir;
4610 case NFROMFD:
4611 p = "<&";
4612 goto redir;
4613 case NFROMTO:
4614 p = "<>";
4615 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004616 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004617 cmdputs(p);
4618 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004619 cmdputs(utoa(n->ndup.dupfd));
4620 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004621 }
4622 n = n->nfile.fname;
4623 goto donode;
4624 }
4625}
4626
4627static char *
4628commandtext(union node *n)
4629{
4630 char *name;
4631
4632 STARTSTACKSTR(cmdnextc);
4633 cmdtxt(n);
4634 name = stackblock();
4635 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4636 name, cmdnextc, cmdnextc));
4637 return ckstrdup(name);
4638}
4639#endif /* JOBS */
4640
4641/*
4642 * Fork off a subshell. If we are doing job control, give the subshell its
4643 * own process group. Jp is a job structure that the job is to be added to.
4644 * N is the command that will be evaluated by the child. Both jp and n may
4645 * be NULL. The mode parameter can be one of the following:
4646 * FORK_FG - Fork off a foreground process.
4647 * FORK_BG - Fork off a background process.
4648 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4649 * process group even if job control is on.
4650 *
4651 * When job control is turned off, background processes have their standard
4652 * input redirected to /dev/null (except for the second and later processes
4653 * in a pipeline).
4654 *
4655 * Called with interrupts off.
4656 */
4657/*
4658 * Clear traps on a fork.
4659 */
4660static void
4661clear_traps(void)
4662{
4663 char **tp;
4664
4665 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004666 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004667 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004668 if (trap_ptr == trap)
4669 free(*tp);
4670 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004671 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004672 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004673 setsignal(tp - trap);
4674 INT_ON;
4675 }
4676 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004677 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004678}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004679
4680/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004681static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004682
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004683/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004684static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004685forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686{
4687 int oldlvl;
4688
4689 TRACE(("Child shell %d\n", getpid()));
4690 oldlvl = shlvl;
4691 shlvl++;
4692
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004693 /* man bash: "Non-builtin commands run by bash have signal handlers
4694 * set to the values inherited by the shell from its parent".
4695 * Do we do it correctly? */
4696
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004697 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004698
4699 if (mode == FORK_NOJOB /* is it `xxx` ? */
4700 && n && n->type == NCMD /* is it single cmd? */
4701 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004702 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004703 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4704 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4705 ) {
4706 TRACE(("Trap hack\n"));
4707 /* Awful hack for `trap` or $(trap).
4708 *
4709 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4710 * contains an example where "trap" is executed in a subshell:
4711 *
4712 * save_traps=$(trap)
4713 * ...
4714 * eval "$save_traps"
4715 *
4716 * Standard does not say that "trap" in subshell shall print
4717 * parent shell's traps. It only says that its output
4718 * must have suitable form, but then, in the above example
4719 * (which is not supposed to be normative), it implies that.
4720 *
4721 * bash (and probably other shell) does implement it
4722 * (traps are reset to defaults, but "trap" still shows them),
4723 * but as a result, "trap" logic is hopelessly messed up:
4724 *
4725 * # trap
4726 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4727 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4728 * # true | trap <--- trap is in subshell - no output (ditto)
4729 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4730 * trap -- 'echo Ho' SIGWINCH
4731 * # echo `(trap)` <--- in subshell in subshell - output
4732 * trap -- 'echo Ho' SIGWINCH
4733 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4734 * trap -- 'echo Ho' SIGWINCH
4735 *
4736 * The rules when to forget and when to not forget traps
4737 * get really complex and nonsensical.
4738 *
4739 * Our solution: ONLY bare $(trap) or `trap` is special.
4740 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004741 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004742 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004743 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004744 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004745 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004746#if JOBS
4747 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004748 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004749 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004750 pid_t pgrp;
4751
4752 if (jp->nprocs == 0)
4753 pgrp = getpid();
4754 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004755 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004756 /* this can fail because we are doing it in the parent also */
4757 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004758 if (mode == FORK_FG)
4759 xtcsetpgrp(ttyfd, pgrp);
4760 setsignal(SIGTSTP);
4761 setsignal(SIGTTOU);
4762 } else
4763#endif
4764 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004765 /* man bash: "When job control is not in effect,
4766 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004767 ignoresig(SIGINT);
4768 ignoresig(SIGQUIT);
4769 if (jp->nprocs == 0) {
4770 close(0);
4771 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004772 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004773 }
4774 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004775 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004776 if (iflag) { /* why if iflag only? */
4777 setsignal(SIGINT);
4778 setsignal(SIGTERM);
4779 }
4780 /* man bash:
4781 * "In all cases, bash ignores SIGQUIT. Non-builtin
4782 * commands run by bash have signal handlers
4783 * set to the values inherited by the shell
4784 * from its parent".
4785 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004786 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004787 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004788#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004789 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004790 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004791 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004792 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004793 /* "jobs": we do not want to clear job list for it,
4794 * instead we remove only _its_ own_ job from job list.
4795 * This makes "jobs .... | cat" more useful.
4796 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004797 freejob(curjob);
4798 return;
4799 }
4800#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004801 for (jp = curjob; jp; jp = jp->prev_job)
4802 freejob(jp);
4803 jobless = 0;
4804}
4805
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004806/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004807#if !JOBS
4808#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4809#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004810static void
4811forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4812{
4813 TRACE(("In parent shell: child = %d\n", pid));
4814 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004815 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4816 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004817 jobless++;
4818 return;
4819 }
4820#if JOBS
4821 if (mode != FORK_NOJOB && jp->jobctl) {
4822 int pgrp;
4823
4824 if (jp->nprocs == 0)
4825 pgrp = pid;
4826 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004827 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004828 /* This can fail because we are doing it in the child also */
4829 setpgid(pid, pgrp);
4830 }
4831#endif
4832 if (mode == FORK_BG) {
4833 backgndpid = pid; /* set $! */
4834 set_curjob(jp, CUR_RUNNING);
4835 }
4836 if (jp) {
4837 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004838 ps->ps_pid = pid;
4839 ps->ps_status = -1;
4840 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004841#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004842 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004843 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004844#endif
4845 }
4846}
4847
4848static int
4849forkshell(struct job *jp, union node *n, int mode)
4850{
4851 int pid;
4852
4853 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4854 pid = fork();
4855 if (pid < 0) {
4856 TRACE(("Fork failed, errno=%d", errno));
4857 if (jp)
4858 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004859 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004860 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004861 if (pid == 0) {
4862 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004863 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004864 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004865 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004866 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004867 return pid;
4868}
4869
4870/*
4871 * Wait for job to finish.
4872 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004873 * Under job control we have the problem that while a child process
4874 * is running interrupts generated by the user are sent to the child
4875 * but not to the shell. This means that an infinite loop started by
4876 * an interactive user may be hard to kill. With job control turned off,
4877 * an interactive user may place an interactive program inside a loop.
4878 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004879 * these interrupts to also abort the loop. The approach we take here
4880 * is to have the shell ignore interrupt signals while waiting for a
4881 * foreground process to terminate, and then send itself an interrupt
4882 * signal if the child process was terminated by an interrupt signal.
4883 * Unfortunately, some programs want to do a bit of cleanup and then
4884 * exit on interrupt; unless these processes terminate themselves by
4885 * sending a signal to themselves (instead of calling exit) they will
4886 * confuse this approach.
4887 *
4888 * Called with interrupts off.
4889 */
4890static int
4891waitforjob(struct job *jp)
4892{
4893 int st;
4894
4895 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004896
4897 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004898 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004899 /* In non-interactive shells, we _can_ get
4900 * a keyboard signal here and be EINTRed,
4901 * but we just loop back, waiting for command to complete.
4902 *
4903 * man bash:
4904 * "If bash is waiting for a command to complete and receives
4905 * a signal for which a trap has been set, the trap
4906 * will not be executed until the command completes."
4907 *
4908 * Reality is that even if trap is not set, bash
4909 * will not act on the signal until command completes.
4910 * Try this. sleep5intoff.c:
4911 * #include <signal.h>
4912 * #include <unistd.h>
4913 * int main() {
4914 * sigset_t set;
4915 * sigemptyset(&set);
4916 * sigaddset(&set, SIGINT);
4917 * sigaddset(&set, SIGQUIT);
4918 * sigprocmask(SIG_BLOCK, &set, NULL);
4919 * sleep(5);
4920 * return 0;
4921 * }
4922 * $ bash -c './sleep5intoff; echo hi'
4923 * ^C^C^C^C <--- pressing ^C once a second
4924 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004925 * $ bash -c './sleep5intoff; echo hi'
4926 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4927 * $ _
4928 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004929 dowait(DOWAIT_BLOCK, jp);
4930 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004931 INT_ON;
4932
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004933 st = getstatus(jp);
4934#if JOBS
4935 if (jp->jobctl) {
4936 xtcsetpgrp(ttyfd, rootpid);
4937 /*
4938 * This is truly gross.
4939 * If we're doing job control, then we did a TIOCSPGRP which
4940 * caused us (the shell) to no longer be in the controlling
4941 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4942 * intuit from the subprocess exit status whether a SIGINT
4943 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4944 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004945 if (jp->sigint) /* TODO: do the same with all signals */
4946 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004947 }
4948 if (jp->state == JOBDONE)
4949#endif
4950 freejob(jp);
4951 return st;
4952}
4953
4954/*
4955 * return 1 if there are stopped jobs, otherwise 0
4956 */
4957static int
4958stoppedjobs(void)
4959{
4960 struct job *jp;
4961 int retval;
4962
4963 retval = 0;
4964 if (job_warning)
4965 goto out;
4966 jp = curjob;
4967 if (jp && jp->state == JOBSTOPPED) {
4968 out2str("You have stopped jobs.\n");
4969 job_warning = 2;
4970 retval++;
4971 }
4972 out:
4973 return retval;
4974}
4975
4976
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004977/* ============ redir.c
4978 *
4979 * Code for dealing with input/output redirection.
4980 */
4981
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004982#undef EMPTY
4983#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004984#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004985#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004986
4987/*
4988 * Open a file in noclobber mode.
4989 * The code was copied from bash.
4990 */
4991static int
4992noclobberopen(const char *fname)
4993{
4994 int r, fd;
4995 struct stat finfo, finfo2;
4996
4997 /*
4998 * If the file exists and is a regular file, return an error
4999 * immediately.
5000 */
5001 r = stat(fname, &finfo);
5002 if (r == 0 && S_ISREG(finfo.st_mode)) {
5003 errno = EEXIST;
5004 return -1;
5005 }
5006
5007 /*
5008 * If the file was not present (r != 0), make sure we open it
5009 * exclusively so that if it is created before we open it, our open
5010 * will fail. Make sure that we do not truncate an existing file.
5011 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5012 * file was not a regular file, we leave O_EXCL off.
5013 */
5014 if (r != 0)
5015 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5016 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5017
5018 /* If the open failed, return the file descriptor right away. */
5019 if (fd < 0)
5020 return fd;
5021
5022 /*
5023 * OK, the open succeeded, but the file may have been changed from a
5024 * non-regular file to a regular file between the stat and the open.
5025 * We are assuming that the O_EXCL open handles the case where FILENAME
5026 * did not exist and is symlinked to an existing file between the stat
5027 * and open.
5028 */
5029
5030 /*
5031 * If we can open it and fstat the file descriptor, and neither check
5032 * revealed that it was a regular file, and the file has not been
5033 * replaced, return the file descriptor.
5034 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005035 if (fstat(fd, &finfo2) == 0
5036 && !S_ISREG(finfo2.st_mode)
5037 && finfo.st_dev == finfo2.st_dev
5038 && finfo.st_ino == finfo2.st_ino
5039 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005040 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005041 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005042
5043 /* The file has been replaced. badness. */
5044 close(fd);
5045 errno = EEXIST;
5046 return -1;
5047}
5048
5049/*
5050 * Handle here documents. Normally we fork off a process to write the
5051 * data to a pipe. If the document is short, we can stuff the data in
5052 * the pipe without forking.
5053 */
5054/* openhere needs this forward reference */
5055static void expandhere(union node *arg, int fd);
5056static int
5057openhere(union node *redir)
5058{
5059 int pip[2];
5060 size_t len = 0;
5061
5062 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005063 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005064 if (redir->type == NHERE) {
5065 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005066 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005067 full_write(pip[1], redir->nhere.doc->narg.text, len);
5068 goto out;
5069 }
5070 }
5071 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005072 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005073 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005074 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5075 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5076 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5077 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005078 signal(SIGPIPE, SIG_DFL);
5079 if (redir->type == NHERE)
5080 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005081 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005082 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005083 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005084 }
5085 out:
5086 close(pip[1]);
5087 return pip[0];
5088}
5089
5090static int
5091openredirect(union node *redir)
5092{
5093 char *fname;
5094 int f;
5095
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02005096 fname = redir->nfile.expfname;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005097 switch (redir->nfile.type) {
5098 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005099 f = open(fname, O_RDONLY);
5100 if (f < 0)
5101 goto eopen;
5102 break;
5103 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005104 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005105 if (f < 0)
5106 goto ecreate;
5107 break;
5108 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005109#if ENABLE_ASH_BASH_COMPAT
5110 case NTO2:
5111#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005112 /* Take care of noclobber mode. */
5113 if (Cflag) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005114 f = noclobberopen(fname);
5115 if (f < 0)
5116 goto ecreate;
5117 break;
5118 }
5119 /* FALLTHROUGH */
5120 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005121 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5122 if (f < 0)
5123 goto ecreate;
5124 break;
5125 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005126 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5127 if (f < 0)
5128 goto ecreate;
5129 break;
5130 default:
5131#if DEBUG
5132 abort();
5133#endif
5134 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005135/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005136// case NTOFD:
5137// case NFROMFD:
5138// f = -1;
5139// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005140 case NHERE:
5141 case NXHERE:
5142 f = openhere(redir);
5143 break;
5144 }
5145
5146 return f;
5147 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005148 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005149 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005150 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005151}
5152
5153/*
5154 * Copy a file descriptor to be >= to. Returns -1
5155 * if the source file descriptor is closed, EMPTY if there are no unused
5156 * file descriptors left.
5157 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005158/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5159 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005160enum {
5161 COPYFD_EXACT = (int)~(INT_MAX),
5162 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5163};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005164static int
5165copyfd(int from, int to)
5166{
5167 int newfd;
5168
Denis Vlasenko5a867312008-07-24 19:46:38 +00005169 if (to & COPYFD_EXACT) {
5170 to &= ~COPYFD_EXACT;
5171 /*if (from != to)*/
5172 newfd = dup2(from, to);
5173 } else {
5174 newfd = fcntl(from, F_DUPFD, to);
5175 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005176 if (newfd < 0) {
5177 if (errno == EMFILE)
5178 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005179 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005180 ash_msg_and_raise_error("%d: %m", from);
5181 }
5182 return newfd;
5183}
5184
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005185/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005186struct two_fd_t {
5187 int orig, copy;
5188};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005189struct redirtab {
5190 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005191 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005192 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005193 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005194};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005195#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005196
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005197static int need_to_remember(struct redirtab *rp, int fd)
5198{
5199 int i;
5200
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005201 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005202 return 0;
5203
5204 for (i = 0; i < rp->pair_count; i++) {
5205 if (rp->two_fd[i].orig == fd) {
5206 /* already remembered */
5207 return 0;
5208 }
5209 }
5210 return 1;
5211}
5212
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005213/* "hidden" fd is a fd used to read scripts, or a copy of such */
5214static int is_hidden_fd(struct redirtab *rp, int fd)
5215{
5216 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005217 struct parsefile *pf;
5218
5219 if (fd == -1)
5220 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005221 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005222 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005223 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005224 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005225 * $ ash # running ash interactively
5226 * $ . ./script.sh
5227 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005228 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005229 * it's still ok to use it: "read" builtin uses it,
5230 * why should we cripple "exec" builtin?
5231 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005232 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005233 return 1;
5234 }
5235 pf = pf->prev;
5236 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005237
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005238 if (!rp)
5239 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005240 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005241 fd |= COPYFD_RESTORE;
5242 for (i = 0; i < rp->pair_count; i++) {
5243 if (rp->two_fd[i].copy == fd) {
5244 return 1;
5245 }
5246 }
5247 return 0;
5248}
5249
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005250/*
5251 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5252 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005253 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005254 */
5255/* flags passed to redirect */
5256#define REDIR_PUSH 01 /* save previous values of file descriptors */
5257#define REDIR_SAVEFD2 03 /* set preverrout */
5258static void
5259redirect(union node *redir, int flags)
5260{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005261 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005262 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005263 int i;
5264 int fd;
5265 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005266 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005267
Denis Vlasenko01631112007-12-16 17:20:38 +00005268 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005269 if (!redir) {
5270 return;
5271 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005272
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005273 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005274 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005275 INT_OFF;
5276 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005277 union node *tmp = redir;
5278 do {
5279 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005280#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005281 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005282 sv_pos++;
5283#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005284 tmp = tmp->nfile.next;
5285 } while (tmp);
5286 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005287 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005288 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005289 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005290 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005291 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005292 while (sv_pos > 0) {
5293 sv_pos--;
5294 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5295 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005296 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005297
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005298 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005299 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005300 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005301 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005302 right_fd = redir->ndup.dupfd;
5303 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005304 /* redirect from/to same file descriptor? */
5305 if (right_fd == fd)
5306 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005307 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005308 if (is_hidden_fd(sv, right_fd)) {
5309 errno = EBADF; /* as if it is closed */
5310 ash_msg_and_raise_error("%d: %m", right_fd);
5311 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005312 newfd = -1;
5313 } else {
5314 newfd = openredirect(redir); /* always >= 0 */
5315 if (fd == newfd) {
5316 /* Descriptor wasn't open before redirect.
5317 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005318 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005319 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005320 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005321 continue;
5322 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005323 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005324#if ENABLE_ASH_BASH_COMPAT
5325 redirect_more:
5326#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005327 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005328 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005329 /* Careful to not accidentally "save"
5330 * to the same fd as right side fd in N>&M */
5331 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5332 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005333/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5334 * are closed in popredir() in the child, preventing them from leaking
5335 * into child. (popredir() also cleans up the mess in case of failures)
5336 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005337 if (i == -1) {
5338 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005339 if (i != EBADF) {
5340 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005341 if (newfd >= 0)
5342 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005343 errno = i;
5344 ash_msg_and_raise_error("%d: %m", fd);
5345 /* NOTREACHED */
5346 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005347 /* EBADF: it is not open - good, remember to close it */
5348 remember_to_close:
5349 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005350 } else { /* fd is open, save its copy */
5351 /* "exec fd>&-" should not close fds
5352 * which point to script file(s).
5353 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005354 if (is_hidden_fd(sv, fd))
5355 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005356 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005357 if (fd == 2)
5358 copied_fd2 = i;
5359 sv->two_fd[sv_pos].orig = fd;
5360 sv->two_fd[sv_pos].copy = i;
5361 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005362 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005363 if (newfd < 0) {
5364 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005365 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005366 /* Don't want to trigger debugging */
5367 if (fd != -1)
5368 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005369 } else {
5370 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005371 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005372 } else if (fd != newfd) { /* move newfd to fd */
5373 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005374#if ENABLE_ASH_BASH_COMPAT
5375 if (!(redir->nfile.type == NTO2 && fd == 2))
5376#endif
5377 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005378 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005379#if ENABLE_ASH_BASH_COMPAT
5380 if (redir->nfile.type == NTO2 && fd == 1) {
5381 /* We already redirected it to fd 1, now copy it to 2 */
5382 newfd = 1;
5383 fd = 2;
5384 goto redirect_more;
5385 }
5386#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005387 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005388
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005389 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005390 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5391 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005392}
5393
5394/*
5395 * Undo the effects of the last redirection.
5396 */
5397static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005398popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005399{
5400 struct redirtab *rp;
5401 int i;
5402
Denis Vlasenko01631112007-12-16 17:20:38 +00005403 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005404 return;
5405 INT_OFF;
5406 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005407 for (i = 0; i < rp->pair_count; i++) {
5408 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005409 int copy = rp->two_fd[i].copy;
5410 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005411 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005412 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005413 continue;
5414 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005415 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005416 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005417 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005418 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005419 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005420 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005421 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005422 }
5423 }
5424 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005425 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005426 free(rp);
5427 INT_ON;
5428}
5429
5430/*
5431 * Undo all redirections. Called on error or interrupt.
5432 */
5433
5434/*
5435 * Discard all saved file descriptors.
5436 */
5437static void
5438clearredir(int drop)
5439{
5440 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005441 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005442 if (!redirlist)
5443 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005444 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005445 }
5446}
5447
5448static int
5449redirectsafe(union node *redir, int flags)
5450{
5451 int err;
5452 volatile int saveint;
5453 struct jmploc *volatile savehandler = exception_handler;
5454 struct jmploc jmploc;
5455
5456 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005457 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5458 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005459 if (!err) {
5460 exception_handler = &jmploc;
5461 redirect(redir, flags);
5462 }
5463 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005464 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005465 longjmp(exception_handler->loc, 1);
5466 RESTORE_INT(saveint);
5467 return err;
5468}
5469
5470
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005471/* ============ Routines to expand arguments to commands
5472 *
5473 * We have to deal with backquotes, shell variables, and file metacharacters.
5474 */
5475
Mike Frysinger98c52642009-04-02 10:02:37 +00005476#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005477static arith_t
5478ash_arith(const char *s)
5479{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005480 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005481 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005482
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005483 math_state.lookupvar = lookupvar;
5484 math_state.setvar = setvar2;
5485 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005486
5487 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005488 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005489 if (math_state.errmsg)
5490 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005491 INT_ON;
5492
5493 return result;
5494}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005495#endif
5496
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005497/*
5498 * expandarg flags
5499 */
5500#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5501#define EXP_TILDE 0x2 /* do normal tilde expansion */
5502#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5503#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5504#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5505#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5506#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5507#define EXP_WORD 0x80 /* expand word in parameter expansion */
5508#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5509/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005510 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005511 */
5512#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5513#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5514#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5515#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5516#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5517
5518/*
5519 * Structure specifying which parts of the string should be searched
5520 * for IFS characters.
5521 */
5522struct ifsregion {
5523 struct ifsregion *next; /* next region in list */
5524 int begoff; /* offset of start of region */
5525 int endoff; /* offset of end of region */
5526 int nulonly; /* search for nul bytes only */
5527};
5528
5529struct arglist {
5530 struct strlist *list;
5531 struct strlist **lastp;
5532};
5533
5534/* output of current string */
5535static char *expdest;
5536/* list of back quote expressions */
5537static struct nodelist *argbackq;
5538/* first struct in list of ifs regions */
5539static struct ifsregion ifsfirst;
5540/* last struct in list */
5541static struct ifsregion *ifslastp;
5542/* holds expanded arg list */
5543static struct arglist exparg;
5544
5545/*
5546 * Our own itoa().
5547 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005548#if !ENABLE_SH_MATH_SUPPORT
5549/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5550typedef long arith_t;
5551# define ARITH_FMT "%ld"
5552#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005553static int
5554cvtnum(arith_t num)
5555{
5556 int len;
5557
5558 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005559 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005560 STADJUST(len, expdest);
5561 return len;
5562}
5563
5564static size_t
5565esclen(const char *start, const char *p)
5566{
5567 size_t esc = 0;
5568
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005569 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005570 esc++;
5571 }
5572 return esc;
5573}
5574
5575/*
5576 * Remove any CTLESC characters from a string.
5577 */
5578static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005579rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005580{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005581 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005582
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005583 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005584 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005585 unsigned protect_against_glob;
5586 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587
5588 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005589 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005590 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005591
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005592 q = p;
5593 r = str;
5594 if (flag & RMESCAPE_ALLOC) {
5595 size_t len = p - str;
5596 size_t fulllen = len + strlen(p) + 1;
5597
5598 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005599 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005601 /* p and str may be invalidated by makestrspace */
5602 str = (char *)stackblock() + strloc;
5603 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005604 } else if (flag & RMESCAPE_HEAP) {
5605 r = ckmalloc(fulllen);
5606 } else {
5607 r = stalloc(fulllen);
5608 }
5609 q = r;
5610 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005611 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005612 }
5613 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005614
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005615 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5616 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005617 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005618 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005619 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005620// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5621// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5622// Note: both inquotes and protect_against_glob only affect whether
5623// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005624 inquotes = ~inquotes;
5625 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005626 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005627 continue;
5628 }
5629 if (*p == '\\') {
5630 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005631 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005632 goto copy;
5633 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005634 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005636 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005637 *q++ = '\\';
5638 }
5639 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005640 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 copy:
5642 *q++ = *p++;
5643 }
5644 *q = '\0';
5645 if (flag & RMESCAPE_GROW) {
5646 expdest = r;
5647 STADJUST(q - r + 1, expdest);
5648 }
5649 return r;
5650}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005651#define pmatch(a, b) !fnmatch((a), (b), 0)
5652
5653/*
5654 * Prepare a pattern for a expmeta (internal glob(3)) call.
5655 *
5656 * Returns an stalloced string.
5657 */
5658static char *
5659preglob(const char *pattern, int quoted, int flag)
5660{
5661 flag |= RMESCAPE_GLOB;
5662 if (quoted) {
5663 flag |= RMESCAPE_QUOTED;
5664 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005665 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005666}
5667
5668/*
5669 * Put a string on the stack.
5670 */
5671static void
5672memtodest(const char *p, size_t len, int syntax, int quotes)
5673{
5674 char *q = expdest;
5675
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005676 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005677
5678 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005679 unsigned char c = *p++;
5680 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005681 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005682 if (quotes) {
5683 int n = SIT(c, syntax);
5684 if (n == CCTL || n == CBACK)
5685 USTPUTC(CTLESC, q);
5686 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005687 USTPUTC(c, q);
5688 }
5689
5690 expdest = q;
5691}
5692
5693static void
5694strtodest(const char *p, int syntax, int quotes)
5695{
5696 memtodest(p, strlen(p), syntax, quotes);
5697}
5698
5699/*
5700 * Record the fact that we have to scan this region of the
5701 * string for IFS characters.
5702 */
5703static void
5704recordregion(int start, int end, int nulonly)
5705{
5706 struct ifsregion *ifsp;
5707
5708 if (ifslastp == NULL) {
5709 ifsp = &ifsfirst;
5710 } else {
5711 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005712 ifsp = ckzalloc(sizeof(*ifsp));
5713 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005714 ifslastp->next = ifsp;
5715 INT_ON;
5716 }
5717 ifslastp = ifsp;
5718 ifslastp->begoff = start;
5719 ifslastp->endoff = end;
5720 ifslastp->nulonly = nulonly;
5721}
5722
5723static void
5724removerecordregions(int endoff)
5725{
5726 if (ifslastp == NULL)
5727 return;
5728
5729 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005730 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005731 struct ifsregion *ifsp;
5732 INT_OFF;
5733 ifsp = ifsfirst.next->next;
5734 free(ifsfirst.next);
5735 ifsfirst.next = ifsp;
5736 INT_ON;
5737 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005738 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005739 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005740 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005741 ifslastp = &ifsfirst;
5742 ifsfirst.endoff = endoff;
5743 }
5744 return;
5745 }
5746
5747 ifslastp = &ifsfirst;
5748 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005749 ifslastp = ifslastp->next;
5750 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005751 struct ifsregion *ifsp;
5752 INT_OFF;
5753 ifsp = ifslastp->next->next;
5754 free(ifslastp->next);
5755 ifslastp->next = ifsp;
5756 INT_ON;
5757 }
5758 if (ifslastp->endoff > endoff)
5759 ifslastp->endoff = endoff;
5760}
5761
5762static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005763exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005764{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005765 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005766 char *name;
5767 struct passwd *pw;
5768 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005769 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005770 int startloc;
5771
5772 name = p + 1;
5773
5774 while ((c = *++p) != '\0') {
5775 switch (c) {
5776 case CTLESC:
5777 return startp;
5778 case CTLQUOTEMARK:
5779 return startp;
5780 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005781 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005782 goto done;
5783 break;
5784 case '/':
5785 case CTLENDVAR:
5786 goto done;
5787 }
5788 }
5789 done:
5790 *p = '\0';
5791 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005792 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005793 } else {
5794 pw = getpwnam(name);
5795 if (pw == NULL)
5796 goto lose;
5797 home = pw->pw_dir;
5798 }
5799 if (!home || !*home)
5800 goto lose;
5801 *p = c;
5802 startloc = expdest - (char *)stackblock();
5803 strtodest(home, SQSYNTAX, quotes);
5804 recordregion(startloc, expdest - (char *)stackblock(), 0);
5805 return p;
5806 lose:
5807 *p = c;
5808 return startp;
5809}
5810
5811/*
5812 * Execute a command inside back quotes. If it's a builtin command, we
5813 * want to save its output in a block obtained from malloc. Otherwise
5814 * we fork off a subprocess and get the output of the command via a pipe.
5815 * Should be called with interrupts off.
5816 */
5817struct backcmd { /* result of evalbackcmd */
5818 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005819 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005820 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005821 struct job *jp; /* job structure for command */
5822};
5823
5824/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005825static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005826#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005827static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005828
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005829static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005830evalbackcmd(union node *n, struct backcmd *result)
5831{
5832 int saveherefd;
5833
5834 result->fd = -1;
5835 result->buf = NULL;
5836 result->nleft = 0;
5837 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005838 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005840
5841 saveherefd = herefd;
5842 herefd = -1;
5843
5844 {
5845 int pip[2];
5846 struct job *jp;
5847
5848 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005849 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005850 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005851 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5852 FORCE_INT_ON;
5853 close(pip[0]);
5854 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005855 /*close(1);*/
5856 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005857 close(pip[1]);
5858 }
5859 eflag = 0;
5860 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5861 /* NOTREACHED */
5862 }
5863 close(pip[1]);
5864 result->fd = pip[0];
5865 result->jp = jp;
5866 }
5867 herefd = saveherefd;
5868 out:
5869 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5870 result->fd, result->buf, result->nleft, result->jp));
5871}
5872
5873/*
5874 * Expand stuff in backwards quotes.
5875 */
5876static void
5877expbackq(union node *cmd, int quoted, int quotes)
5878{
5879 struct backcmd in;
5880 int i;
5881 char buf[128];
5882 char *p;
5883 char *dest;
5884 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005885 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005886 struct stackmark smark;
5887
5888 INT_OFF;
5889 setstackmark(&smark);
5890 dest = expdest;
5891 startloc = dest - (char *)stackblock();
5892 grabstackstr(dest);
5893 evalbackcmd(cmd, &in);
5894 popstackmark(&smark);
5895
5896 p = in.buf;
5897 i = in.nleft;
5898 if (i == 0)
5899 goto read;
5900 for (;;) {
5901 memtodest(p, i, syntax, quotes);
5902 read:
5903 if (in.fd < 0)
5904 break;
Denys Vlasenko80542ba2011-05-08 21:23:43 +02005905 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005906 TRACE(("expbackq: read returns %d\n", i));
5907 if (i <= 0)
5908 break;
5909 p = buf;
5910 }
5911
Denis Vlasenko60818682007-09-28 22:07:23 +00005912 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005913 if (in.fd >= 0) {
5914 close(in.fd);
5915 back_exitstatus = waitforjob(in.jp);
5916 }
5917 INT_ON;
5918
5919 /* Eat all trailing newlines */
5920 dest = expdest;
5921 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5922 STUNPUTC(dest);
5923 expdest = dest;
5924
5925 if (quoted == 0)
5926 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005927 TRACE(("evalbackq: size:%d:'%.*s'\n",
5928 (int)((dest - (char *)stackblock()) - startloc),
5929 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005930 stackblock() + startloc));
5931}
5932
Mike Frysinger98c52642009-04-02 10:02:37 +00005933#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005934/*
5935 * Expand arithmetic expression. Backup to start of expression,
5936 * evaluate, place result in (backed up) result, adjust string position.
5937 */
5938static void
5939expari(int quotes)
5940{
5941 char *p, *start;
5942 int begoff;
5943 int flag;
5944 int len;
5945
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005946 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005947
5948 /*
5949 * This routine is slightly over-complicated for
5950 * efficiency. Next we scan backwards looking for the
5951 * start of arithmetic.
5952 */
5953 start = stackblock();
5954 p = expdest - 1;
5955 *p = '\0';
5956 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005957 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958 int esc;
5959
Denys Vlasenkocd716832009-11-28 22:14:02 +01005960 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005961 p--;
5962#if DEBUG
5963 if (p < start) {
5964 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5965 }
5966#endif
5967 }
5968
5969 esc = esclen(start, p);
5970 if (!(esc % 2)) {
5971 break;
5972 }
5973
5974 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005975 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005976
5977 begoff = p - start;
5978
5979 removerecordregions(begoff);
5980
5981 flag = p[1];
5982
5983 expdest = p;
5984
5985 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005986 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005987
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005988 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005989
5990 if (flag != '"')
5991 recordregion(begoff, begoff + len, 0);
5992}
5993#endif
5994
5995/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005996static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005997
5998/*
5999 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6000 * characters to allow for further processing. Otherwise treat
6001 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006002 *
6003 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6004 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6005 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006 */
6007static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006008argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006009{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006010 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006011 '=',
6012 ':',
6013 CTLQUOTEMARK,
6014 CTLENDVAR,
6015 CTLESC,
6016 CTLVAR,
6017 CTLBACKQ,
6018 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006019#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006020 CTLENDARI,
6021#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006022 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 };
6024 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006025 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6026 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006027 int inquotes;
6028 size_t length;
6029 int startloc;
6030
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006031 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006032 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006033 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006034 reject++;
6035 }
6036 inquotes = 0;
6037 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006038 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006039 char *q;
6040
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006041 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006042 tilde:
6043 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006044 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006045 q++;
6046 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006047 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006048 }
6049 start:
6050 startloc = expdest - (char *)stackblock();
6051 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006052 unsigned char c;
6053
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006055 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006056 if (c) {
6057 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006058 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006059 ) {
6060 /* c == '=' || c == ':' || c == CTLENDARI */
6061 length++;
6062 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006063 }
6064 if (length > 0) {
6065 int newloc;
6066 expdest = stack_nputstr(p, length, expdest);
6067 newloc = expdest - (char *)stackblock();
6068 if (breakall && !inquotes && newloc > startloc) {
6069 recordregion(startloc, newloc, 0);
6070 }
6071 startloc = newloc;
6072 }
6073 p += length + 1;
6074 length = 0;
6075
6076 switch (c) {
6077 case '\0':
6078 goto breakloop;
6079 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006080 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006081 p--;
6082 continue;
6083 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006084 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006085 reject++;
6086 /* fall through */
6087 case ':':
6088 /*
6089 * sort of a hack - expand tildes in variable
6090 * assignments (after the first '=' and after ':'s).
6091 */
6092 if (*--p == '~') {
6093 goto tilde;
6094 }
6095 continue;
6096 }
6097
6098 switch (c) {
6099 case CTLENDVAR: /* ??? */
6100 goto breakloop;
6101 case CTLQUOTEMARK:
6102 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006103 if (!inquotes
6104 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006105 && ( p[4] == (char)CTLQUOTEMARK
6106 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006107 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006108 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006109 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006110 goto start;
6111 }
6112 inquotes = !inquotes;
6113 addquote:
6114 if (quotes) {
6115 p--;
6116 length++;
6117 startloc++;
6118 }
6119 break;
6120 case CTLESC:
6121 startloc++;
6122 length++;
6123 goto addquote;
6124 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006125 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006126 p = evalvar(p, flags, var_str_list);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006127 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006128 goto start;
6129 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006130 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006131 case CTLBACKQ|CTLQUOTE:
6132 expbackq(argbackq->n, c, quotes);
6133 argbackq = argbackq->next;
6134 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006135#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006136 case CTLENDARI:
6137 p--;
6138 expari(quotes);
6139 goto start;
6140#endif
6141 }
6142 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006143 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006144}
6145
6146static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006147scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6148 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006149{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006150 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006151 char c;
6152
6153 loc = startp;
6154 loc2 = rmesc;
6155 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006156 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006157 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006158
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006159 c = *loc2;
6160 if (zero) {
6161 *loc2 = '\0';
6162 s = rmesc;
6163 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006164 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006165
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006167 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006169 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006170 loc++;
6171 loc++;
6172 loc2++;
6173 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006174 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175}
6176
6177static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006178scanright(char *startp, char *rmesc, char *rmescend,
6179 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006180{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006181#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6182 int try2optimize = match_at_start;
6183#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 int esc = 0;
6185 char *loc;
6186 char *loc2;
6187
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006188 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6189 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6190 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6191 * Logic:
6192 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6193 * and on each iteration they go back two/one char until they reach the beginning.
6194 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6195 */
6196 /* TODO: document in what other circumstances we are called. */
6197
6198 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006199 int match;
6200 char c = *loc2;
6201 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006202 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006203 *loc2 = '\0';
6204 s = rmesc;
6205 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006206 match = pmatch(pattern, s);
6207 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006208 *loc2 = c;
6209 if (match)
6210 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006211#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6212 if (try2optimize) {
6213 /* Maybe we can optimize this:
6214 * if pattern ends with unescaped *, we can avoid checking
6215 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6216 * it wont match truncated "raw_value_of_" strings too.
6217 */
6218 unsigned plen = strlen(pattern);
6219 /* Does it end with "*"? */
6220 if (plen != 0 && pattern[--plen] == '*') {
6221 /* "xxxx*" is not escaped */
6222 /* "xxx\*" is escaped */
6223 /* "xx\\*" is not escaped */
6224 /* "x\\\*" is escaped */
6225 int slashes = 0;
6226 while (plen != 0 && pattern[--plen] == '\\')
6227 slashes++;
6228 if (!(slashes & 1))
6229 break; /* ends with unescaped "*" */
6230 }
6231 try2optimize = 0;
6232 }
6233#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006234 loc--;
6235 if (quotes) {
6236 if (--esc < 0) {
6237 esc = esclen(startp, loc);
6238 }
6239 if (esc % 2) {
6240 esc--;
6241 loc--;
6242 }
6243 }
6244 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006245 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006246}
6247
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006248static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006249static void
6250varunset(const char *end, const char *var, const char *umsg, int varflags)
6251{
6252 const char *msg;
6253 const char *tail;
6254
6255 tail = nullstr;
6256 msg = "parameter not set";
6257 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006258 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006259 if (varflags & VSNUL)
6260 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006261 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006262 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006263 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006264 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006265 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266}
6267
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006268#if ENABLE_ASH_BASH_COMPAT
6269static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006270parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006271{
6272 char *idx, *repl = NULL;
6273 unsigned char c;
6274
Denys Vlasenko16149002010-08-06 22:06:21 +02006275 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006276 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006277 idx = arg;
6278 while (1) {
6279 c = *arg;
6280 if (!c)
6281 break;
6282 if (c == '/') {
6283 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006284 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006285 repl = idx + 1;
6286 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006287 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006288 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006289 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006290 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006291 /*
6292 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6293 * The result is a_\_z_c (not a\_\_z_c)!
6294 *
6295 * Enable debug prints in this function and you'll see:
6296 * ash: arg:'\\b/_\\_z_' varflags:d
6297 * ash: pattern:'\\b' repl:'_\_z_'
6298 * That is, \\b is interpreted as \\b, but \\_ as \_!
6299 * IOW: search pattern and replace string treat backslashes
6300 * differently! That is the reason why we check repl below:
6301 */
6302 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6303 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006304 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006305 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006306 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006307
6308 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006309}
6310#endif /* ENABLE_ASH_BASH_COMPAT */
6311
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006312static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006313subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006314 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006315{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006316 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006317 char *startp;
6318 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006319 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006320 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006321 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006322 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006323 int saveherefd = herefd;
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02006324 int amount, resetloc;
6325 IF_ASH_BASH_COMPAT(int workloc;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006326 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006327 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006328
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006329 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6330 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006331
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006333 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6334 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006335 STPUTC('\0', expdest);
6336 herefd = saveherefd;
6337 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006338 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006339
6340 switch (subtype) {
6341 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006342 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006343 amount = startp - expdest;
6344 STADJUST(amount, expdest);
6345 return startp;
6346
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006347 case VSQUESTION:
6348 varunset(p, varname, startp, varflags);
6349 /* NOTREACHED */
6350
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006351#if ENABLE_ASH_BASH_COMPAT
6352 case VSSUBSTR:
6353 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006354 /* Read POS in ${var:POS:LEN} */
6355 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006356 len = str - startp - 1;
6357
6358 /* *loc != '\0', guaranteed by parser */
6359 if (quotes) {
6360 char *ptr;
6361
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006362 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006363 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006364 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006365 len--;
6366 ptr++;
6367 }
6368 }
6369 }
6370 orig_len = len;
6371
6372 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006373 /* ${var::LEN} */
6374 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006375 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006376 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006377 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006378 while (*loc && *loc != ':') {
6379 /* TODO?
6380 * bash complains on: var=qwe; echo ${var:1a:123}
6381 if (!isdigit(*loc))
6382 ash_msg_and_raise_error(msg_illnum, str);
6383 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006384 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006385 }
6386 if (*loc++ == ':') {
6387 len = number(loc);
6388 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006389 }
6390 if (pos >= orig_len) {
6391 pos = 0;
6392 len = 0;
6393 }
6394 if (len > (orig_len - pos))
6395 len = orig_len - pos;
6396
6397 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006398 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006399 str++;
6400 }
6401 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006402 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006403 *loc++ = *str++;
6404 *loc++ = *str++;
6405 }
6406 *loc = '\0';
6407 amount = loc - expdest;
6408 STADJUST(amount, expdest);
6409 return loc;
6410#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006412
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006413 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006414
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006415 /* We'll comeback here if we grow the stack while handling
6416 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6417 * stack will need rebasing, and we'll need to remove our work
6418 * areas each time
6419 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006420 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006421
6422 amount = expdest - ((char *)stackblock() + resetloc);
6423 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006424 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006425
6426 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006427 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006428 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006429 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006430 if (rmesc != startp) {
6431 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006432 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433 }
6434 }
6435 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006436 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006437 preglob(str, varflags & VSQUOTE, 0);
6438
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006439#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02006440 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006441 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006442 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006443
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006444 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006445 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006446 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006447 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006448 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006449 }
6450
6451 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006452 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006453 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006454
6455 len = 0;
6456 idx = startp;
6457 end = str - 1;
6458 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006459 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006461 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006462 if (!loc) {
6463 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006464 char *restart_detect = stackblock();
6465 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006466 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006467 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006468 idx++;
6469 len++;
6470 STPUTC(*idx, expdest);
6471 }
6472 if (stackblock() != restart_detect)
6473 goto restart;
6474 idx++;
6475 len++;
6476 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006477 /* continue; - prone to quadratic behavior, smarter code: */
6478 if (idx >= end)
6479 break;
6480 if (str[0] == '*') {
6481 /* Pattern is "*foo". If "*foo" does not match "long_string",
6482 * it would never match "ong_string" etc, no point in trying.
6483 */
6484 goto skip_matching;
6485 }
6486 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006487 }
6488
6489 if (subtype == VSREPLACEALL) {
6490 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006491 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006492 idx++;
6493 idx++;
6494 rmesc++;
6495 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006496 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006497 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006498 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006499
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006500 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006501 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006502 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006503 if (quotes && *loc == '\\') {
6504 STPUTC(CTLESC, expdest);
6505 len++;
6506 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006507 STPUTC(*loc, expdest);
6508 if (stackblock() != restart_detect)
6509 goto restart;
6510 len++;
6511 }
6512
6513 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006514 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006515 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006516 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006517 STPUTC(*idx, expdest);
6518 if (stackblock() != restart_detect)
6519 goto restart;
6520 len++;
6521 idx++;
6522 }
6523 break;
6524 }
6525 }
6526
6527 /* We've put the replaced text into a buffer at workloc, now
6528 * move it to the right place and adjust the stack.
6529 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006530 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006531 startp = (char *)stackblock() + startloc;
6532 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006533 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006534 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006535 STADJUST(-amount, expdest);
6536 return startp;
6537 }
6538#endif /* ENABLE_ASH_BASH_COMPAT */
6539
6540 subtype -= VSTRIMRIGHT;
6541#if DEBUG
6542 if (subtype < 0 || subtype > 7)
6543 abort();
6544#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006545 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006546 zero = subtype >> 1;
6547 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6548 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6549
6550 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6551 if (loc) {
6552 if (zero) {
6553 memmove(startp, loc, str - loc);
6554 loc = startp + (str - loc) - 1;
6555 }
6556 *loc = '\0';
6557 amount = loc - expdest;
6558 STADJUST(amount, expdest);
6559 }
6560 return loc;
6561}
6562
6563/*
6564 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006565 * name parameter (examples):
6566 * ash -c 'echo $1' name:'1='
6567 * ash -c 'echo $qwe' name:'qwe='
6568 * ash -c 'echo $$' name:'$='
6569 * ash -c 'echo ${$}' name:'$='
6570 * ash -c 'echo ${$##q}' name:'$=q'
6571 * ash -c 'echo ${#$}' name:'$='
6572 * note: examples with bad shell syntax:
6573 * ash -c 'echo ${#$1}' name:'$=1'
6574 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006575 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006576static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006577varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006578{
Mike Frysinger98c52642009-04-02 10:02:37 +00006579 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006580 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006581 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006582 int sepq = 0;
6583 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006584 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006585 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006586 int quoted = varflags & VSQUOTE;
6587 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589 switch (*name) {
6590 case '$':
6591 num = rootpid;
6592 goto numvar;
6593 case '?':
6594 num = exitstatus;
6595 goto numvar;
6596 case '#':
6597 num = shellparam.nparam;
6598 goto numvar;
6599 case '!':
6600 num = backgndpid;
6601 if (num == 0)
6602 return -1;
6603 numvar:
6604 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006605 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006606 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006607 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006608 for (i = NOPTS - 1; i >= 0; i--) {
6609 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006610 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006611 len++;
6612 }
6613 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006614 check_1char_name:
6615#if 0
6616 /* handles cases similar to ${#$1} */
6617 if (name[2] != '\0')
6618 raise_error_syntax("bad substitution");
6619#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006620 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006621 case '@': {
6622 char **ap;
6623 int sep;
6624
6625 if (quoted && (flags & EXP_FULL)) {
6626 /* note: this is not meant as PEOF value */
6627 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006628 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006629 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006630 /* fall through */
6631 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006632 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006633 i = SIT(sep, syntax);
6634 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006635 sepq = 1;
6636 param:
6637 ap = shellparam.p;
6638 if (!ap)
6639 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006640 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641 size_t partlen;
6642
6643 partlen = strlen(p);
6644 len += partlen;
6645
6646 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6647 memtodest(p, partlen, syntax, quotes);
6648
6649 if (*ap && sep) {
6650 char *q;
6651
6652 len++;
6653 if (subtype == VSPLUS || subtype == VSLENGTH) {
6654 continue;
6655 }
6656 q = expdest;
6657 if (sepq)
6658 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006659 /* note: may put NUL despite sep != 0
6660 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006661 STPUTC(sep, q);
6662 expdest = q;
6663 }
6664 }
6665 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006666 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006667 case '0':
6668 case '1':
6669 case '2':
6670 case '3':
6671 case '4':
6672 case '5':
6673 case '6':
6674 case '7':
6675 case '8':
6676 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006677 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006678 if (num < 0 || num > shellparam.nparam)
6679 return -1;
6680 p = num ? shellparam.p[num - 1] : arg0;
6681 goto value;
6682 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006683 /* NB: name has form "VAR=..." */
6684
6685 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6686 * which should be considered before we check variables. */
6687 if (var_str_list) {
6688 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6689 p = NULL;
6690 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006691 char *str, *eq;
6692 str = var_str_list->text;
6693 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006694 if (!eq) /* stop at first non-assignment */
6695 break;
6696 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006697 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006698 && strncmp(str, name, name_len) == 0
6699 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006700 p = eq;
6701 /* goto value; - WRONG! */
6702 /* think "A=1 A=2 B=$A" */
6703 }
6704 var_str_list = var_str_list->next;
6705 } while (var_str_list);
6706 if (p)
6707 goto value;
6708 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006709 p = lookupvar(name);
6710 value:
6711 if (!p)
6712 return -1;
6713
6714 len = strlen(p);
6715 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6716 memtodest(p, len, syntax, quotes);
6717 return len;
6718 }
6719
6720 if (subtype == VSPLUS || subtype == VSLENGTH)
6721 STADJUST(-len, expdest);
6722 return len;
6723}
6724
6725/*
6726 * Expand a variable, and return a pointer to the next character in the
6727 * input string.
6728 */
6729static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006730evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006731{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006732 char varflags;
6733 char subtype;
6734 char quoted;
6735 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006736 char *var;
6737 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006738 int startloc;
6739 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006740
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006741 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006742 subtype = varflags & VSTYPE;
6743 quoted = varflags & VSQUOTE;
6744 var = p;
6745 easy = (!quoted || (*var == '@' && shellparam.nparam));
6746 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006747 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748
6749 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006750 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006751 if (varflags & VSNUL)
6752 varlen--;
6753
6754 if (subtype == VSPLUS) {
6755 varlen = -1 - varlen;
6756 goto vsplus;
6757 }
6758
6759 if (subtype == VSMINUS) {
6760 vsplus:
6761 if (varlen < 0) {
6762 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006763 p,
6764 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006765 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006766 );
6767 goto end;
6768 }
6769 if (easy)
6770 goto record;
6771 goto end;
6772 }
6773
6774 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6775 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006776 if (subevalvar(p, var, /* strloc: */ 0,
6777 subtype, startloc, varflags,
6778 /* quotes: */ 0,
6779 var_str_list)
6780 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006781 varflags &= ~VSNUL;
6782 /*
6783 * Remove any recorded regions beyond
6784 * start of variable
6785 */
6786 removerecordregions(startloc);
6787 goto again;
6788 }
6789 goto end;
6790 }
6791 if (easy)
6792 goto record;
6793 goto end;
6794 }
6795
6796 if (varlen < 0 && uflag)
6797 varunset(p, var, 0, 0);
6798
6799 if (subtype == VSLENGTH) {
6800 cvtnum(varlen > 0 ? varlen : 0);
6801 goto record;
6802 }
6803
6804 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006805 if (easy)
6806 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006807 goto end;
6808 }
6809
6810#if DEBUG
6811 switch (subtype) {
6812 case VSTRIMLEFT:
6813 case VSTRIMLEFTMAX:
6814 case VSTRIMRIGHT:
6815 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006816#if ENABLE_ASH_BASH_COMPAT
6817 case VSSUBSTR:
6818 case VSREPLACE:
6819 case VSREPLACEALL:
6820#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006821 break;
6822 default:
6823 abort();
6824 }
6825#endif
6826
6827 if (varlen >= 0) {
6828 /*
6829 * Terminate the string and start recording the pattern
6830 * right after it
6831 */
6832 STPUTC('\0', expdest);
6833 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006834 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006835 startloc, varflags,
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006836 /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006837 var_str_list)
6838 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006839 int amount = expdest - (
6840 (char *)stackblock() + patloc - 1
6841 );
6842 STADJUST(-amount, expdest);
6843 }
6844 /* Remove any recorded regions beyond start of variable */
6845 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006846 record:
6847 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006848 }
6849
6850 end:
6851 if (subtype != VSNORMAL) { /* skip to end of alternative */
6852 int nesting = 1;
6853 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006854 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006855 if (c == CTLESC)
6856 p++;
6857 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6858 if (varlen >= 0)
6859 argbackq = argbackq->next;
6860 } else if (c == CTLVAR) {
6861 if ((*p++ & VSTYPE) != VSNORMAL)
6862 nesting++;
6863 } else if (c == CTLENDVAR) {
6864 if (--nesting == 0)
6865 break;
6866 }
6867 }
6868 }
6869 return p;
6870}
6871
6872/*
6873 * Break the argument string into pieces based upon IFS and add the
6874 * strings to the argument list. The regions of the string to be
6875 * searched for IFS characters have been stored by recordregion.
6876 */
6877static void
6878ifsbreakup(char *string, struct arglist *arglist)
6879{
6880 struct ifsregion *ifsp;
6881 struct strlist *sp;
6882 char *start;
6883 char *p;
6884 char *q;
6885 const char *ifs, *realifs;
6886 int ifsspc;
6887 int nulonly;
6888
6889 start = string;
6890 if (ifslastp != NULL) {
6891 ifsspc = 0;
6892 nulonly = 0;
6893 realifs = ifsset() ? ifsval() : defifs;
6894 ifsp = &ifsfirst;
6895 do {
6896 p = string + ifsp->begoff;
6897 nulonly = ifsp->nulonly;
6898 ifs = nulonly ? nullstr : realifs;
6899 ifsspc = 0;
6900 while (p < string + ifsp->endoff) {
6901 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006902 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006903 p++;
6904 if (!strchr(ifs, *p)) {
6905 p++;
6906 continue;
6907 }
6908 if (!nulonly)
6909 ifsspc = (strchr(defifs, *p) != NULL);
6910 /* Ignore IFS whitespace at start */
6911 if (q == start && ifsspc) {
6912 p++;
6913 start = p;
6914 continue;
6915 }
6916 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006917 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006918 sp->text = start;
6919 *arglist->lastp = sp;
6920 arglist->lastp = &sp->next;
6921 p++;
6922 if (!nulonly) {
6923 for (;;) {
6924 if (p >= string + ifsp->endoff) {
6925 break;
6926 }
6927 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006928 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006929 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006930 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006931 p = q;
6932 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006933 }
6934 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006935 if (ifsspc) {
6936 p++;
6937 ifsspc = 0;
6938 } else {
6939 p = q;
6940 break;
6941 }
6942 } else
6943 p++;
6944 }
6945 }
6946 start = p;
6947 } /* while */
6948 ifsp = ifsp->next;
6949 } while (ifsp != NULL);
6950 if (nulonly)
6951 goto add;
6952 }
6953
6954 if (!*start)
6955 return;
6956
6957 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006958 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006959 sp->text = start;
6960 *arglist->lastp = sp;
6961 arglist->lastp = &sp->next;
6962}
6963
6964static void
6965ifsfree(void)
6966{
6967 struct ifsregion *p;
6968
6969 INT_OFF;
6970 p = ifsfirst.next;
6971 do {
6972 struct ifsregion *ifsp;
6973 ifsp = p->next;
6974 free(p);
6975 p = ifsp;
6976 } while (p);
6977 ifslastp = NULL;
6978 ifsfirst.next = NULL;
6979 INT_ON;
6980}
6981
6982/*
6983 * Add a file name to the list.
6984 */
6985static void
6986addfname(const char *name)
6987{
6988 struct strlist *sp;
6989
Denis Vlasenko597906c2008-02-20 16:38:54 +00006990 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006991 sp->text = ststrdup(name);
6992 *exparg.lastp = sp;
6993 exparg.lastp = &sp->next;
6994}
6995
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006996/*
6997 * Do metacharacter (i.e. *, ?, [...]) expansion.
6998 */
6999static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007000expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007001{
7002 char *p;
7003 const char *cp;
7004 char *start;
7005 char *endname;
7006 int metaflag;
7007 struct stat statb;
7008 DIR *dirp;
7009 struct dirent *dp;
7010 int atend;
7011 int matchdot;
7012
7013 metaflag = 0;
7014 start = name;
7015 for (p = name; *p; p++) {
7016 if (*p == '*' || *p == '?')
7017 metaflag = 1;
7018 else if (*p == '[') {
7019 char *q = p + 1;
7020 if (*q == '!')
7021 q++;
7022 for (;;) {
7023 if (*q == '\\')
7024 q++;
7025 if (*q == '/' || *q == '\0')
7026 break;
7027 if (*++q == ']') {
7028 metaflag = 1;
7029 break;
7030 }
7031 }
7032 } else if (*p == '\\')
7033 p++;
7034 else if (*p == '/') {
7035 if (metaflag)
7036 goto out;
7037 start = p + 1;
7038 }
7039 }
7040 out:
7041 if (metaflag == 0) { /* we've reached the end of the file name */
7042 if (enddir != expdir)
7043 metaflag++;
7044 p = name;
7045 do {
7046 if (*p == '\\')
7047 p++;
7048 *enddir++ = *p;
7049 } while (*p++);
7050 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7051 addfname(expdir);
7052 return;
7053 }
7054 endname = p;
7055 if (name < start) {
7056 p = name;
7057 do {
7058 if (*p == '\\')
7059 p++;
7060 *enddir++ = *p++;
7061 } while (p < start);
7062 }
7063 if (enddir == expdir) {
7064 cp = ".";
7065 } else if (enddir == expdir + 1 && *expdir == '/') {
7066 cp = "/";
7067 } else {
7068 cp = expdir;
7069 enddir[-1] = '\0';
7070 }
7071 dirp = opendir(cp);
7072 if (dirp == NULL)
7073 return;
7074 if (enddir != expdir)
7075 enddir[-1] = '/';
7076 if (*endname == 0) {
7077 atend = 1;
7078 } else {
7079 atend = 0;
7080 *endname++ = '\0';
7081 }
7082 matchdot = 0;
7083 p = start;
7084 if (*p == '\\')
7085 p++;
7086 if (*p == '.')
7087 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007088 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007089 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007090 continue;
7091 if (pmatch(start, dp->d_name)) {
7092 if (atend) {
7093 strcpy(enddir, dp->d_name);
7094 addfname(expdir);
7095 } else {
7096 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7097 continue;
7098 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007099 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007100 }
7101 }
7102 }
7103 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007104 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007105 endname[-1] = '/';
7106}
7107
7108static struct strlist *
7109msort(struct strlist *list, int len)
7110{
7111 struct strlist *p, *q = NULL;
7112 struct strlist **lpp;
7113 int half;
7114 int n;
7115
7116 if (len <= 1)
7117 return list;
7118 half = len >> 1;
7119 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007120 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007121 q = p;
7122 p = p->next;
7123 }
7124 q->next = NULL; /* terminate first half of list */
7125 q = msort(list, half); /* sort first half of list */
7126 p = msort(p, len - half); /* sort second half */
7127 lpp = &list;
7128 for (;;) {
7129#if ENABLE_LOCALE_SUPPORT
7130 if (strcoll(p->text, q->text) < 0)
7131#else
7132 if (strcmp(p->text, q->text) < 0)
7133#endif
7134 {
7135 *lpp = p;
7136 lpp = &p->next;
7137 p = *lpp;
7138 if (p == NULL) {
7139 *lpp = q;
7140 break;
7141 }
7142 } else {
7143 *lpp = q;
7144 lpp = &q->next;
7145 q = *lpp;
7146 if (q == NULL) {
7147 *lpp = p;
7148 break;
7149 }
7150 }
7151 }
7152 return list;
7153}
7154
7155/*
7156 * Sort the results of file name expansion. It calculates the number of
7157 * strings to sort and then calls msort (short for merge sort) to do the
7158 * work.
7159 */
7160static struct strlist *
7161expsort(struct strlist *str)
7162{
7163 int len;
7164 struct strlist *sp;
7165
7166 len = 0;
7167 for (sp = str; sp; sp = sp->next)
7168 len++;
7169 return msort(str, len);
7170}
7171
7172static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007173expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007174{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007175 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007176 '*', '?', '[', 0
7177 };
7178 /* TODO - EXP_REDIR */
7179
7180 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007181 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007182 struct strlist **savelastp;
7183 struct strlist *sp;
7184 char *p;
7185
7186 if (fflag)
7187 goto nometa;
7188 if (!strpbrk(str->text, metachars))
7189 goto nometa;
7190 savelastp = exparg.lastp;
7191
7192 INT_OFF;
7193 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7194 {
7195 int i = strlen(str->text);
7196 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7197 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007198 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007199 free(expdir);
7200 if (p != str->text)
7201 free(p);
7202 INT_ON;
7203 if (exparg.lastp == savelastp) {
7204 /*
7205 * no matches
7206 */
7207 nometa:
7208 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007209 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007210 exparg.lastp = &str->next;
7211 } else {
7212 *exparg.lastp = NULL;
7213 *savelastp = sp = expsort(*savelastp);
7214 while (sp->next != NULL)
7215 sp = sp->next;
7216 exparg.lastp = &sp->next;
7217 }
7218 str = str->next;
7219 }
7220}
7221
7222/*
7223 * Perform variable substitution and command substitution on an argument,
7224 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7225 * perform splitting and file name expansion. When arglist is NULL, perform
7226 * here document expansion.
7227 */
7228static void
7229expandarg(union node *arg, struct arglist *arglist, int flag)
7230{
7231 struct strlist *sp;
7232 char *p;
7233
7234 argbackq = arg->narg.backquote;
7235 STARTSTACKSTR(expdest);
7236 ifsfirst.next = NULL;
7237 ifslastp = NULL;
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007238 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007239 argstr(arg->narg.text, flag,
7240 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007241 p = _STPUTC('\0', expdest);
7242 expdest = p - 1;
7243 if (arglist == NULL) {
7244 return; /* here document expanded */
7245 }
7246 p = grabstackstr(p);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007247 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007248 exparg.lastp = &exparg.list;
7249 /*
7250 * TODO - EXP_REDIR
7251 */
7252 if (flag & EXP_FULL) {
7253 ifsbreakup(p, &exparg);
7254 *exparg.lastp = NULL;
7255 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007256 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007257 } else {
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007258 if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007259 rmescapes(p, 0);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007260 TRACE(("expandarg: rmescapes:'%s'\n", p));
7261 }
Denis Vlasenko597906c2008-02-20 16:38:54 +00007262 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007263 sp->text = p;
7264 *exparg.lastp = sp;
7265 exparg.lastp = &sp->next;
7266 }
7267 if (ifsfirst.next)
7268 ifsfree();
7269 *exparg.lastp = NULL;
7270 if (exparg.list) {
7271 *arglist->lastp = exparg.list;
7272 arglist->lastp = exparg.lastp;
7273 }
7274}
7275
7276/*
7277 * Expand shell variables and backquotes inside a here document.
7278 */
7279static void
7280expandhere(union node *arg, int fd)
7281{
7282 herefd = fd;
7283 expandarg(arg, (struct arglist *)NULL, 0);
7284 full_write(fd, stackblock(), expdest - (char *)stackblock());
7285}
7286
7287/*
7288 * Returns true if the pattern matches the string.
7289 */
7290static int
7291patmatch(char *pattern, const char *string)
7292{
7293 return pmatch(preglob(pattern, 0, 0), string);
7294}
7295
7296/*
7297 * See if a pattern matches in a case statement.
7298 */
7299static int
7300casematch(union node *pattern, char *val)
7301{
7302 struct stackmark smark;
7303 int result;
7304
7305 setstackmark(&smark);
7306 argbackq = pattern->narg.backquote;
7307 STARTSTACKSTR(expdest);
7308 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007309 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7310 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007311 STACKSTRNUL(expdest);
7312 result = patmatch(stackblock(), val);
7313 popstackmark(&smark);
7314 return result;
7315}
7316
7317
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007318/* ============ find_command */
7319
7320struct builtincmd {
7321 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007322 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007323 /* unsigned flags; */
7324};
7325#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007326/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007327 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007328#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007329#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007330
7331struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007332 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007333 union param {
7334 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007335 /* index >= 0 for commands without path (slashes) */
7336 /* (TODO: what exactly does the value mean? PATH position?) */
7337 /* index == -1 for commands with slashes */
7338 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007339 const struct builtincmd *cmd;
7340 struct funcnode *func;
7341 } u;
7342};
7343/* values of cmdtype */
7344#define CMDUNKNOWN -1 /* no entry in table for command */
7345#define CMDNORMAL 0 /* command is an executable program */
7346#define CMDFUNCTION 1 /* command is a shell function */
7347#define CMDBUILTIN 2 /* command is a shell builtin */
7348
7349/* action to find_command() */
7350#define DO_ERR 0x01 /* prints errors */
7351#define DO_ABS 0x02 /* checks absolute paths */
7352#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7353#define DO_ALTPATH 0x08 /* using alternate path */
7354#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7355
7356static void find_command(char *, struct cmdentry *, int, const char *);
7357
7358
7359/* ============ Hashing commands */
7360
7361/*
7362 * When commands are first encountered, they are entered in a hash table.
7363 * This ensures that a full path search will not have to be done for them
7364 * on each invocation.
7365 *
7366 * We should investigate converting to a linear search, even though that
7367 * would make the command name "hash" a misnomer.
7368 */
7369
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007370struct tblentry {
7371 struct tblentry *next; /* next entry in hash chain */
7372 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007373 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007374 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007375 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007376};
7377
Denis Vlasenko01631112007-12-16 17:20:38 +00007378static struct tblentry **cmdtable;
7379#define INIT_G_cmdtable() do { \
7380 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7381} while (0)
7382
7383static int builtinloc = -1; /* index in path of %builtin, or -1 */
7384
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007385
7386static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007387tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007388{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007389#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007390 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007391 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007392 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007393 while (*envp)
7394 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007395 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007396 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007397 /* re-exec ourselves with the new arguments */
7398 execve(bb_busybox_exec_path, argv, envp);
7399 /* If they called chroot or otherwise made the binary no longer
7400 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401 }
7402#endif
7403
7404 repeat:
7405#ifdef SYSV
7406 do {
7407 execve(cmd, argv, envp);
7408 } while (errno == EINTR);
7409#else
7410 execve(cmd, argv, envp);
7411#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007412 if (cmd == (char*) bb_busybox_exec_path) {
7413 /* We already visited ENOEXEC branch below, don't do it again */
7414//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007415 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007416 return;
7417 }
7418 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007419 /* Run "cmd" as a shell script:
7420 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7421 * "If the execve() function fails with ENOEXEC, the shell
7422 * shall execute a command equivalent to having a shell invoked
7423 * with the command name as its first operand,
7424 * with any remaining arguments passed to the new shell"
7425 *
7426 * That is, do not use $SHELL, user's shell, or /bin/sh;
7427 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01007428 *
7429 * Note that bash reads ~80 chars of the file, and if it sees
7430 * a zero byte before it sees newline, it doesn't try to
7431 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01007432 * message and exit code 126. For one, this prevents attempts
7433 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007434 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007435 char **ap;
7436 char **new;
7437
7438 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007439 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007440 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7441 new[0] = (char*) "ash";
7442 new[1] = cmd;
7443 ap = new + 2;
7444 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007445 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007446 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007447 argv = new;
7448 goto repeat;
7449 }
7450}
7451
7452/*
7453 * Exec a program. Never returns. If you change this routine, you may
7454 * have to change the find_command routine as well.
7455 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007456static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007457static void
7458shellexec(char **argv, const char *path, int idx)
7459{
7460 char *cmdname;
7461 int e;
7462 char **envp;
7463 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007464 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007465
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007466 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007467 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007468 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007469#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007470 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007471#endif
7472 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007473 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007474 if (applet_no >= 0) {
7475 /* We tried execing ourself, but it didn't work.
7476 * Maybe /proc/self/exe doesn't exist?
7477 * Try $PATH search.
7478 */
7479 goto try_PATH;
7480 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007481 e = errno;
7482 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007483 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007484 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007485 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007486 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007487 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007488 if (errno != ENOENT && errno != ENOTDIR)
7489 e = errno;
7490 }
7491 stunalloc(cmdname);
7492 }
7493 }
7494
7495 /* Map to POSIX errors */
7496 switch (e) {
7497 case EACCES:
7498 exerrno = 126;
7499 break;
7500 case ENOENT:
7501 exerrno = 127;
7502 break;
7503 default:
7504 exerrno = 2;
7505 break;
7506 }
7507 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007508 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7509 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007510 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7511 /* NOTREACHED */
7512}
7513
7514static void
7515printentry(struct tblentry *cmdp)
7516{
7517 int idx;
7518 const char *path;
7519 char *name;
7520
7521 idx = cmdp->param.index;
7522 path = pathval();
7523 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007524 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007525 stunalloc(name);
7526 } while (--idx >= 0);
7527 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7528}
7529
7530/*
7531 * Clear out command entries. The argument specifies the first entry in
7532 * PATH which has changed.
7533 */
7534static void
7535clearcmdentry(int firstchange)
7536{
7537 struct tblentry **tblp;
7538 struct tblentry **pp;
7539 struct tblentry *cmdp;
7540
7541 INT_OFF;
7542 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7543 pp = tblp;
7544 while ((cmdp = *pp) != NULL) {
7545 if ((cmdp->cmdtype == CMDNORMAL &&
7546 cmdp->param.index >= firstchange)
7547 || (cmdp->cmdtype == CMDBUILTIN &&
7548 builtinloc >= firstchange)
7549 ) {
7550 *pp = cmdp->next;
7551 free(cmdp);
7552 } else {
7553 pp = &cmdp->next;
7554 }
7555 }
7556 }
7557 INT_ON;
7558}
7559
7560/*
7561 * Locate a command in the command hash table. If "add" is nonzero,
7562 * add the command to the table if it is not already present. The
7563 * variable "lastcmdentry" is set to point to the address of the link
7564 * pointing to the entry, so that delete_cmd_entry can delete the
7565 * entry.
7566 *
7567 * Interrupts must be off if called with add != 0.
7568 */
7569static struct tblentry **lastcmdentry;
7570
7571static struct tblentry *
7572cmdlookup(const char *name, int add)
7573{
7574 unsigned int hashval;
7575 const char *p;
7576 struct tblentry *cmdp;
7577 struct tblentry **pp;
7578
7579 p = name;
7580 hashval = (unsigned char)*p << 4;
7581 while (*p)
7582 hashval += (unsigned char)*p++;
7583 hashval &= 0x7FFF;
7584 pp = &cmdtable[hashval % CMDTABLESIZE];
7585 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7586 if (strcmp(cmdp->cmdname, name) == 0)
7587 break;
7588 pp = &cmdp->next;
7589 }
7590 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007591 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7592 + strlen(name)
7593 /* + 1 - already done because
7594 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007595 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007596 cmdp->cmdtype = CMDUNKNOWN;
7597 strcpy(cmdp->cmdname, name);
7598 }
7599 lastcmdentry = pp;
7600 return cmdp;
7601}
7602
7603/*
7604 * Delete the command entry returned on the last lookup.
7605 */
7606static void
7607delete_cmd_entry(void)
7608{
7609 struct tblentry *cmdp;
7610
7611 INT_OFF;
7612 cmdp = *lastcmdentry;
7613 *lastcmdentry = cmdp->next;
7614 if (cmdp->cmdtype == CMDFUNCTION)
7615 freefunc(cmdp->param.func);
7616 free(cmdp);
7617 INT_ON;
7618}
7619
7620/*
7621 * Add a new command entry, replacing any existing command entry for
7622 * the same name - except special builtins.
7623 */
7624static void
7625addcmdentry(char *name, struct cmdentry *entry)
7626{
7627 struct tblentry *cmdp;
7628
7629 cmdp = cmdlookup(name, 1);
7630 if (cmdp->cmdtype == CMDFUNCTION) {
7631 freefunc(cmdp->param.func);
7632 }
7633 cmdp->cmdtype = entry->cmdtype;
7634 cmdp->param = entry->u;
7635 cmdp->rehash = 0;
7636}
7637
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007638static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007639hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007640{
7641 struct tblentry **pp;
7642 struct tblentry *cmdp;
7643 int c;
7644 struct cmdentry entry;
7645 char *name;
7646
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007647 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007648 clearcmdentry(0);
7649 return 0;
7650 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007651
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007652 if (*argptr == NULL) {
7653 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7654 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7655 if (cmdp->cmdtype == CMDNORMAL)
7656 printentry(cmdp);
7657 }
7658 }
7659 return 0;
7660 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007661
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007662 c = 0;
7663 while ((name = *argptr) != NULL) {
7664 cmdp = cmdlookup(name, 0);
7665 if (cmdp != NULL
7666 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007667 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7668 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007669 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007670 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007671 find_command(name, &entry, DO_ERR, pathval());
7672 if (entry.cmdtype == CMDUNKNOWN)
7673 c = 1;
7674 argptr++;
7675 }
7676 return c;
7677}
7678
7679/*
7680 * Called when a cd is done. Marks all commands so the next time they
7681 * are executed they will be rehashed.
7682 */
7683static void
7684hashcd(void)
7685{
7686 struct tblentry **pp;
7687 struct tblentry *cmdp;
7688
7689 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7690 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007691 if (cmdp->cmdtype == CMDNORMAL
7692 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007693 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007694 && builtinloc > 0)
7695 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007696 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007697 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007698 }
7699 }
7700}
7701
7702/*
7703 * Fix command hash table when PATH changed.
7704 * Called before PATH is changed. The argument is the new value of PATH;
7705 * pathval() still returns the old value at this point.
7706 * Called with interrupts off.
7707 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007708static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007709changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007710{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007711 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007712 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007713 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007714 int idx_bltin;
7715
7716 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007717 firstchange = 9999; /* assume no change */
7718 idx = 0;
7719 idx_bltin = -1;
7720 for (;;) {
7721 if (*old != *new) {
7722 firstchange = idx;
7723 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007724 || (*old == ':' && *new == '\0')
7725 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007726 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007727 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007728 old = new; /* ignore subsequent differences */
7729 }
7730 if (*new == '\0')
7731 break;
7732 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7733 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007734 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007735 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007736 new++;
7737 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007738 }
7739 if (builtinloc < 0 && idx_bltin >= 0)
7740 builtinloc = idx_bltin; /* zap builtins */
7741 if (builtinloc >= 0 && idx_bltin < 0)
7742 firstchange = 0;
7743 clearcmdentry(firstchange);
7744 builtinloc = idx_bltin;
7745}
7746
7747#define TEOF 0
7748#define TNL 1
7749#define TREDIR 2
7750#define TWORD 3
7751#define TSEMI 4
7752#define TBACKGND 5
7753#define TAND 6
7754#define TOR 7
7755#define TPIPE 8
7756#define TLP 9
7757#define TRP 10
7758#define TENDCASE 11
7759#define TENDBQUOTE 12
7760#define TNOT 13
7761#define TCASE 14
7762#define TDO 15
7763#define TDONE 16
7764#define TELIF 17
7765#define TELSE 18
7766#define TESAC 19
7767#define TFI 20
7768#define TFOR 21
7769#define TIF 22
7770#define TIN 23
7771#define TTHEN 24
7772#define TUNTIL 25
7773#define TWHILE 26
7774#define TBEGIN 27
7775#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007776typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007777
7778/* first char is indicating which tokens mark the end of a list */
7779static const char *const tokname_array[] = {
7780 "\1end of file",
7781 "\0newline",
7782 "\0redirection",
7783 "\0word",
7784 "\0;",
7785 "\0&",
7786 "\0&&",
7787 "\0||",
7788 "\0|",
7789 "\0(",
7790 "\1)",
7791 "\1;;",
7792 "\1`",
7793#define KWDOFFSET 13
7794 /* the following are keywords */
7795 "\0!",
7796 "\0case",
7797 "\1do",
7798 "\1done",
7799 "\1elif",
7800 "\1else",
7801 "\1esac",
7802 "\1fi",
7803 "\0for",
7804 "\0if",
7805 "\0in",
7806 "\1then",
7807 "\0until",
7808 "\0while",
7809 "\0{",
7810 "\1}",
7811};
7812
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007813/* Wrapper around strcmp for qsort/bsearch/... */
7814static int
7815pstrcmp(const void *a, const void *b)
7816{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007817 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007818}
7819
7820static const char *const *
7821findkwd(const char *s)
7822{
7823 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007824 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7825 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007826}
7827
7828/*
7829 * Locate and print what a word is...
7830 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007831static int
7832describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007833{
7834 struct cmdentry entry;
7835 struct tblentry *cmdp;
7836#if ENABLE_ASH_ALIAS
7837 const struct alias *ap;
7838#endif
7839 const char *path = pathval();
7840
7841 if (describe_command_verbose) {
7842 out1str(command);
7843 }
7844
7845 /* First look at the keywords */
7846 if (findkwd(command)) {
7847 out1str(describe_command_verbose ? " is a shell keyword" : command);
7848 goto out;
7849 }
7850
7851#if ENABLE_ASH_ALIAS
7852 /* Then look at the aliases */
7853 ap = lookupalias(command, 0);
7854 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007855 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007856 out1str("alias ");
7857 printalias(ap);
7858 return 0;
7859 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007860 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007861 goto out;
7862 }
7863#endif
7864 /* Then check if it is a tracked alias */
7865 cmdp = cmdlookup(command, 0);
7866 if (cmdp != NULL) {
7867 entry.cmdtype = cmdp->cmdtype;
7868 entry.u = cmdp->param;
7869 } else {
7870 /* Finally use brute force */
7871 find_command(command, &entry, DO_ABS, path);
7872 }
7873
7874 switch (entry.cmdtype) {
7875 case CMDNORMAL: {
7876 int j = entry.u.index;
7877 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007878 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007879 p = command;
7880 } else {
7881 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007882 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007883 stunalloc(p);
7884 } while (--j >= 0);
7885 }
7886 if (describe_command_verbose) {
7887 out1fmt(" is%s %s",
7888 (cmdp ? " a tracked alias for" : nullstr), p
7889 );
7890 } else {
7891 out1str(p);
7892 }
7893 break;
7894 }
7895
7896 case CMDFUNCTION:
7897 if (describe_command_verbose) {
7898 out1str(" is a shell function");
7899 } else {
7900 out1str(command);
7901 }
7902 break;
7903
7904 case CMDBUILTIN:
7905 if (describe_command_verbose) {
7906 out1fmt(" is a %sshell builtin",
7907 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7908 "special " : nullstr
7909 );
7910 } else {
7911 out1str(command);
7912 }
7913 break;
7914
7915 default:
7916 if (describe_command_verbose) {
7917 out1str(": not found\n");
7918 }
7919 return 127;
7920 }
7921 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007922 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923 return 0;
7924}
7925
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007926static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007927typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007928{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007929 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007930 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007931 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007932
Denis Vlasenko46846e22007-05-20 13:08:31 +00007933 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007934 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007935 i++;
7936 verbose = 0;
7937 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007938 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007939 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007940 }
7941 return err;
7942}
7943
7944#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007945static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007946commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007947{
7948 int c;
7949 enum {
7950 VERIFY_BRIEF = 1,
7951 VERIFY_VERBOSE = 2,
7952 } verify = 0;
7953
7954 while ((c = nextopt("pvV")) != '\0')
7955 if (c == 'V')
7956 verify |= VERIFY_VERBOSE;
7957 else if (c == 'v')
7958 verify |= VERIFY_BRIEF;
7959#if DEBUG
7960 else if (c != 'p')
7961 abort();
7962#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007963 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7964 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007965 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007966 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007967
7968 return 0;
7969}
7970#endif
7971
7972
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007973/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007974
Denis Vlasenko340299a2008-11-21 10:36:36 +00007975static int funcblocksize; /* size of structures in function */
7976static int funcstringsize; /* size of strings in node */
7977static void *funcblock; /* block to allocate function from */
7978static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007979
Eric Andersencb57d552001-06-28 07:25:16 +00007980/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007981#define EV_EXIT 01 /* exit after evaluating tree */
7982#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007983#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007984
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007985static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007986 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7987 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7988 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7989 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7990 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7991 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7992 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7993 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7994 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7995 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7996 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7997 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7998 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7999 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8000 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8001 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8002 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008003#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008004 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008005#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008006 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8007 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8008 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8009 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8010 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8011 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8012 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8013 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8014 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008015};
8016
8017static void calcsize(union node *n);
8018
8019static void
8020sizenodelist(struct nodelist *lp)
8021{
8022 while (lp) {
8023 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8024 calcsize(lp->n);
8025 lp = lp->next;
8026 }
8027}
8028
8029static void
8030calcsize(union node *n)
8031{
8032 if (n == NULL)
8033 return;
8034 funcblocksize += nodesize[n->type];
8035 switch (n->type) {
8036 case NCMD:
8037 calcsize(n->ncmd.redirect);
8038 calcsize(n->ncmd.args);
8039 calcsize(n->ncmd.assign);
8040 break;
8041 case NPIPE:
8042 sizenodelist(n->npipe.cmdlist);
8043 break;
8044 case NREDIR:
8045 case NBACKGND:
8046 case NSUBSHELL:
8047 calcsize(n->nredir.redirect);
8048 calcsize(n->nredir.n);
8049 break;
8050 case NAND:
8051 case NOR:
8052 case NSEMI:
8053 case NWHILE:
8054 case NUNTIL:
8055 calcsize(n->nbinary.ch2);
8056 calcsize(n->nbinary.ch1);
8057 break;
8058 case NIF:
8059 calcsize(n->nif.elsepart);
8060 calcsize(n->nif.ifpart);
8061 calcsize(n->nif.test);
8062 break;
8063 case NFOR:
8064 funcstringsize += strlen(n->nfor.var) + 1;
8065 calcsize(n->nfor.body);
8066 calcsize(n->nfor.args);
8067 break;
8068 case NCASE:
8069 calcsize(n->ncase.cases);
8070 calcsize(n->ncase.expr);
8071 break;
8072 case NCLIST:
8073 calcsize(n->nclist.body);
8074 calcsize(n->nclist.pattern);
8075 calcsize(n->nclist.next);
8076 break;
8077 case NDEFUN:
8078 case NARG:
8079 sizenodelist(n->narg.backquote);
8080 funcstringsize += strlen(n->narg.text) + 1;
8081 calcsize(n->narg.next);
8082 break;
8083 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008084#if ENABLE_ASH_BASH_COMPAT
8085 case NTO2:
8086#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008087 case NCLOBBER:
8088 case NFROM:
8089 case NFROMTO:
8090 case NAPPEND:
8091 calcsize(n->nfile.fname);
8092 calcsize(n->nfile.next);
8093 break;
8094 case NTOFD:
8095 case NFROMFD:
8096 calcsize(n->ndup.vname);
8097 calcsize(n->ndup.next);
8098 break;
8099 case NHERE:
8100 case NXHERE:
8101 calcsize(n->nhere.doc);
8102 calcsize(n->nhere.next);
8103 break;
8104 case NNOT:
8105 calcsize(n->nnot.com);
8106 break;
8107 };
8108}
8109
8110static char *
8111nodeckstrdup(char *s)
8112{
8113 char *rtn = funcstring;
8114
8115 strcpy(funcstring, s);
8116 funcstring += strlen(s) + 1;
8117 return rtn;
8118}
8119
8120static union node *copynode(union node *);
8121
8122static struct nodelist *
8123copynodelist(struct nodelist *lp)
8124{
8125 struct nodelist *start;
8126 struct nodelist **lpp;
8127
8128 lpp = &start;
8129 while (lp) {
8130 *lpp = funcblock;
8131 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8132 (*lpp)->n = copynode(lp->n);
8133 lp = lp->next;
8134 lpp = &(*lpp)->next;
8135 }
8136 *lpp = NULL;
8137 return start;
8138}
8139
8140static union node *
8141copynode(union node *n)
8142{
8143 union node *new;
8144
8145 if (n == NULL)
8146 return NULL;
8147 new = funcblock;
8148 funcblock = (char *) funcblock + nodesize[n->type];
8149
8150 switch (n->type) {
8151 case NCMD:
8152 new->ncmd.redirect = copynode(n->ncmd.redirect);
8153 new->ncmd.args = copynode(n->ncmd.args);
8154 new->ncmd.assign = copynode(n->ncmd.assign);
8155 break;
8156 case NPIPE:
8157 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008158 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008159 break;
8160 case NREDIR:
8161 case NBACKGND:
8162 case NSUBSHELL:
8163 new->nredir.redirect = copynode(n->nredir.redirect);
8164 new->nredir.n = copynode(n->nredir.n);
8165 break;
8166 case NAND:
8167 case NOR:
8168 case NSEMI:
8169 case NWHILE:
8170 case NUNTIL:
8171 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8172 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8173 break;
8174 case NIF:
8175 new->nif.elsepart = copynode(n->nif.elsepart);
8176 new->nif.ifpart = copynode(n->nif.ifpart);
8177 new->nif.test = copynode(n->nif.test);
8178 break;
8179 case NFOR:
8180 new->nfor.var = nodeckstrdup(n->nfor.var);
8181 new->nfor.body = copynode(n->nfor.body);
8182 new->nfor.args = copynode(n->nfor.args);
8183 break;
8184 case NCASE:
8185 new->ncase.cases = copynode(n->ncase.cases);
8186 new->ncase.expr = copynode(n->ncase.expr);
8187 break;
8188 case NCLIST:
8189 new->nclist.body = copynode(n->nclist.body);
8190 new->nclist.pattern = copynode(n->nclist.pattern);
8191 new->nclist.next = copynode(n->nclist.next);
8192 break;
8193 case NDEFUN:
8194 case NARG:
8195 new->narg.backquote = copynodelist(n->narg.backquote);
8196 new->narg.text = nodeckstrdup(n->narg.text);
8197 new->narg.next = copynode(n->narg.next);
8198 break;
8199 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008200#if ENABLE_ASH_BASH_COMPAT
8201 case NTO2:
8202#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008203 case NCLOBBER:
8204 case NFROM:
8205 case NFROMTO:
8206 case NAPPEND:
8207 new->nfile.fname = copynode(n->nfile.fname);
8208 new->nfile.fd = n->nfile.fd;
8209 new->nfile.next = copynode(n->nfile.next);
8210 break;
8211 case NTOFD:
8212 case NFROMFD:
8213 new->ndup.vname = copynode(n->ndup.vname);
8214 new->ndup.dupfd = n->ndup.dupfd;
8215 new->ndup.fd = n->ndup.fd;
8216 new->ndup.next = copynode(n->ndup.next);
8217 break;
8218 case NHERE:
8219 case NXHERE:
8220 new->nhere.doc = copynode(n->nhere.doc);
8221 new->nhere.fd = n->nhere.fd;
8222 new->nhere.next = copynode(n->nhere.next);
8223 break;
8224 case NNOT:
8225 new->nnot.com = copynode(n->nnot.com);
8226 break;
8227 };
8228 new->type = n->type;
8229 return new;
8230}
8231
8232/*
8233 * Make a copy of a parse tree.
8234 */
8235static struct funcnode *
8236copyfunc(union node *n)
8237{
8238 struct funcnode *f;
8239 size_t blocksize;
8240
8241 funcblocksize = offsetof(struct funcnode, n);
8242 funcstringsize = 0;
8243 calcsize(n);
8244 blocksize = funcblocksize;
8245 f = ckmalloc(blocksize + funcstringsize);
8246 funcblock = (char *) f + offsetof(struct funcnode, n);
8247 funcstring = (char *) f + blocksize;
8248 copynode(n);
8249 f->count = 0;
8250 return f;
8251}
8252
8253/*
8254 * Define a shell function.
8255 */
8256static void
8257defun(char *name, union node *func)
8258{
8259 struct cmdentry entry;
8260
8261 INT_OFF;
8262 entry.cmdtype = CMDFUNCTION;
8263 entry.u.func = copyfunc(func);
8264 addcmdentry(name, &entry);
8265 INT_ON;
8266}
8267
Denis Vlasenko4b875702009-03-19 13:30:04 +00008268/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008269#define SKIPBREAK (1 << 0)
8270#define SKIPCONT (1 << 1)
8271#define SKIPFUNC (1 << 2)
8272#define SKIPFILE (1 << 3)
8273#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008274static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008275static int skipcount; /* number of levels to skip */
8276static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008277static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008278
Denis Vlasenko4b875702009-03-19 13:30:04 +00008279/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008280static int evalstring(char *s, int mask);
8281
Denis Vlasenko4b875702009-03-19 13:30:04 +00008282/* Called to execute a trap.
8283 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008284 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008285 *
8286 * Perhaps we should avoid entering new trap handlers
8287 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008288 */
8289static int
8290dotrap(void)
8291{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008292 uint8_t *g;
8293 int sig;
8294 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008295
8296 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008297 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008298 xbarrier();
8299
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008300 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008301 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8302 int want_exexit;
8303 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008304
Denis Vlasenko4b875702009-03-19 13:30:04 +00008305 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008306 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008307 t = trap[sig];
8308 /* non-trapped SIGINT is handled separately by raise_interrupt,
8309 * don't upset it by resetting gotsig[SIGINT-1] */
8310 if (sig == SIGINT && !t)
8311 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008312
8313 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008314 *g = 0;
8315 if (!t)
8316 continue;
8317 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008318 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008319 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008320 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008321 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008322 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008323 }
8324
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008325 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008326 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008327}
8328
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008329/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008330static void evalloop(union node *, int);
8331static void evalfor(union node *, int);
8332static void evalcase(union node *, int);
8333static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008334static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008335static void evalpipe(union node *, int);
8336static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008337static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008338static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008339
Eric Andersen62483552001-07-10 06:09:16 +00008340/*
Eric Andersenc470f442003-07-28 09:56:35 +00008341 * Evaluate a parse tree. The value is left in the global variable
8342 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008343 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008344static void
Eric Andersenc470f442003-07-28 09:56:35 +00008345evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008346{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008347 struct jmploc *volatile savehandler = exception_handler;
8348 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008349 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008350 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008351 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008352 int int_level;
8353
8354 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008355
Eric Andersenc470f442003-07-28 09:56:35 +00008356 if (n == NULL) {
8357 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008358 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008359 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008360 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008361
8362 exception_handler = &jmploc;
8363 {
8364 int err = setjmp(jmploc.loc);
8365 if (err) {
8366 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008367 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008368 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8369 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008370 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008371 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008372 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008373 TRACE(("exception %d in evaltree, propagating err=%d\n",
8374 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008375 exception_handler = savehandler;
8376 longjmp(exception_handler->loc, err);
8377 }
8378 }
8379
Eric Andersenc470f442003-07-28 09:56:35 +00008380 switch (n->type) {
8381 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008382#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008383 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008384 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008385 break;
8386#endif
8387 case NNOT:
8388 evaltree(n->nnot.com, EV_TESTED);
8389 status = !exitstatus;
8390 goto setstatus;
8391 case NREDIR:
8392 expredir(n->nredir.redirect);
8393 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8394 if (!status) {
8395 evaltree(n->nredir.n, flags & EV_TESTED);
8396 status = exitstatus;
8397 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008398 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008399 goto setstatus;
8400 case NCMD:
8401 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008402 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008403 if (eflag && !(flags & EV_TESTED))
8404 checkexit = ~0;
8405 goto calleval;
8406 case NFOR:
8407 evalfn = evalfor;
8408 goto calleval;
8409 case NWHILE:
8410 case NUNTIL:
8411 evalfn = evalloop;
8412 goto calleval;
8413 case NSUBSHELL:
8414 case NBACKGND:
8415 evalfn = evalsubshell;
8416 goto calleval;
8417 case NPIPE:
8418 evalfn = evalpipe;
8419 goto checkexit;
8420 case NCASE:
8421 evalfn = evalcase;
8422 goto calleval;
8423 case NAND:
8424 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008425 case NSEMI: {
8426
Eric Andersenc470f442003-07-28 09:56:35 +00008427#if NAND + 1 != NOR
8428#error NAND + 1 != NOR
8429#endif
8430#if NOR + 1 != NSEMI
8431#error NOR + 1 != NSEMI
8432#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008433 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008434 evaltree(
8435 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008436 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008437 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008438 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008439 break;
8440 if (!evalskip) {
8441 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008442 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008443 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008444 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008445 evalfn(n, flags);
8446 break;
8447 }
8448 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008449 }
Eric Andersenc470f442003-07-28 09:56:35 +00008450 case NIF:
8451 evaltree(n->nif.test, EV_TESTED);
8452 if (evalskip)
8453 break;
8454 if (exitstatus == 0) {
8455 n = n->nif.ifpart;
8456 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008457 }
8458 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008459 n = n->nif.elsepart;
8460 goto evaln;
8461 }
8462 goto success;
8463 case NDEFUN:
8464 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008465 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008466 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008467 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008468 exitstatus = status;
8469 break;
8470 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008471
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008472 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008473 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008474
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008475 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008476 /* Order of checks below is important:
8477 * signal handlers trigger before exit caused by "set -e".
8478 */
8479 if (pending_sig && dotrap())
8480 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008481 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008482 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008483
8484 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008485 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008486 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008487 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008488
8489 RESTORE_INT(int_level);
8490 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008491}
8492
Eric Andersenc470f442003-07-28 09:56:35 +00008493#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8494static
8495#endif
8496void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8497
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008498static void
Eric Andersenc470f442003-07-28 09:56:35 +00008499evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008500{
8501 int status;
8502
8503 loopnest++;
8504 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008505 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008506 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008507 int i;
8508
Eric Andersencb57d552001-06-28 07:25:16 +00008509 evaltree(n->nbinary.ch1, EV_TESTED);
8510 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008511 skipping:
8512 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008513 evalskip = 0;
8514 continue;
8515 }
8516 if (evalskip == SKIPBREAK && --skipcount <= 0)
8517 evalskip = 0;
8518 break;
8519 }
Eric Andersenc470f442003-07-28 09:56:35 +00008520 i = exitstatus;
8521 if (n->type != NWHILE)
8522 i = !i;
8523 if (i != 0)
8524 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008525 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008526 status = exitstatus;
8527 if (evalskip)
8528 goto skipping;
8529 }
8530 loopnest--;
8531 exitstatus = status;
8532}
8533
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008534static void
Eric Andersenc470f442003-07-28 09:56:35 +00008535evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008536{
8537 struct arglist arglist;
8538 union node *argp;
8539 struct strlist *sp;
8540 struct stackmark smark;
8541
8542 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008543 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008544 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008545 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008546 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008547 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008548 if (evalskip)
8549 goto out;
8550 }
8551 *arglist.lastp = NULL;
8552
8553 exitstatus = 0;
8554 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008555 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008556 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008557 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008558 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008559 if (evalskip) {
8560 if (evalskip == SKIPCONT && --skipcount <= 0) {
8561 evalskip = 0;
8562 continue;
8563 }
8564 if (evalskip == SKIPBREAK && --skipcount <= 0)
8565 evalskip = 0;
8566 break;
8567 }
8568 }
8569 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008570 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008571 popstackmark(&smark);
8572}
8573
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008574static void
Eric Andersenc470f442003-07-28 09:56:35 +00008575evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008576{
8577 union node *cp;
8578 union node *patp;
8579 struct arglist arglist;
8580 struct stackmark smark;
8581
8582 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008583 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008584 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008585 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008586 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008587 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8588 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008589 if (casematch(patp, arglist.list->text)) {
8590 if (evalskip == 0) {
8591 evaltree(cp->nclist.body, flags);
8592 }
8593 goto out;
8594 }
8595 }
8596 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008597 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008598 popstackmark(&smark);
8599}
8600
Eric Andersenc470f442003-07-28 09:56:35 +00008601/*
8602 * Kick off a subshell to evaluate a tree.
8603 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008604static void
Eric Andersenc470f442003-07-28 09:56:35 +00008605evalsubshell(union node *n, int flags)
8606{
8607 struct job *jp;
8608 int backgnd = (n->type == NBACKGND);
8609 int status;
8610
8611 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008612 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008613 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008614 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008615 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008616 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008617 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008618 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008619 flags |= EV_EXIT;
8620 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008621 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008622 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008623 redirect(n->nredir.redirect, 0);
8624 evaltreenr(n->nredir.n, flags);
8625 /* never returns */
8626 }
8627 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008628 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008629 status = waitforjob(jp);
8630 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008631 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008632}
8633
Eric Andersenc470f442003-07-28 09:56:35 +00008634/*
8635 * Compute the names of the files in a redirection list.
8636 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008637static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008638static void
8639expredir(union node *n)
8640{
8641 union node *redir;
8642
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008643 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008644 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008645
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008646 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008647 fn.lastp = &fn.list;
8648 switch (redir->type) {
8649 case NFROMTO:
8650 case NFROM:
8651 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008652#if ENABLE_ASH_BASH_COMPAT
8653 case NTO2:
8654#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008655 case NCLOBBER:
8656 case NAPPEND:
8657 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008658 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denis Vlasenko559691a2008-10-05 18:39:31 +00008659#if ENABLE_ASH_BASH_COMPAT
8660 store_expfname:
8661#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01008662#if 0
8663// By the design of stack allocator, the loop of this kind:
8664// while true; do while true; do break; done </dev/null; done
8665// will look like a memory leak: ash plans to free expfname's
8666// of "/dev/null" as soon as it finishes running the loop
8667// (in this case, never).
8668// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01008669 if (redir->nfile.expfname)
8670 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01008671// It results in corrupted state of stacked allocations.
8672#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008673 redir->nfile.expfname = fn.list->text;
8674 break;
8675 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008676 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008677 if (redir->ndup.vname) {
8678 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008679 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008680 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008681#if ENABLE_ASH_BASH_COMPAT
8682//FIXME: we used expandarg with different args!
8683 if (!isdigit_str9(fn.list->text)) {
8684 /* >&file, not >&fd */
8685 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8686 ash_msg_and_raise_error("redir error");
8687 redir->type = NTO2;
8688 goto store_expfname;
8689 }
8690#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008691 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008692 }
8693 break;
8694 }
8695 }
8696}
8697
Eric Andersencb57d552001-06-28 07:25:16 +00008698/*
Eric Andersencb57d552001-06-28 07:25:16 +00008699 * Evaluate a pipeline. All the processes in the pipeline are children
8700 * of the process creating the pipeline. (This differs from some versions
8701 * of the shell, which make the last process in a pipeline the parent
8702 * of all the rest.)
8703 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008704static void
Eric Andersenc470f442003-07-28 09:56:35 +00008705evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008706{
8707 struct job *jp;
8708 struct nodelist *lp;
8709 int pipelen;
8710 int prevfd;
8711 int pip[2];
8712
Eric Andersenc470f442003-07-28 09:56:35 +00008713 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008714 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008715 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008716 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008717 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008718 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008719 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008720 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008721 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008722 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008723 pip[1] = -1;
8724 if (lp->next) {
8725 if (pipe(pip) < 0) {
8726 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008727 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008728 }
8729 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008730 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008731 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008732 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008733 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008734 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008735 if (prevfd > 0) {
8736 dup2(prevfd, 0);
8737 close(prevfd);
8738 }
8739 if (pip[1] > 1) {
8740 dup2(pip[1], 1);
8741 close(pip[1]);
8742 }
Eric Andersenc470f442003-07-28 09:56:35 +00008743 evaltreenr(lp->n, flags);
8744 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008745 }
8746 if (prevfd >= 0)
8747 close(prevfd);
8748 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008749 /* Don't want to trigger debugging */
8750 if (pip[1] != -1)
8751 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008752 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008753 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008754 exitstatus = waitforjob(jp);
8755 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008756 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008757 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008758}
8759
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008760/*
8761 * Controls whether the shell is interactive or not.
8762 */
8763static void
8764setinteractive(int on)
8765{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008766 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008767
8768 if (++on == is_interactive)
8769 return;
8770 is_interactive = on;
8771 setsignal(SIGINT);
8772 setsignal(SIGQUIT);
8773 setsignal(SIGTERM);
8774#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8775 if (is_interactive > 1) {
8776 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008777 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008778
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008779 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008780 /* note: ash and hush share this string */
8781 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008782 "Enter 'help' for a list of built-in commands."
8783 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008784 bb_banner,
8785 "built-in shell (ash)"
8786 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008787 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008788 }
8789 }
8790#endif
8791}
8792
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008793static void
8794optschanged(void)
8795{
8796#if DEBUG
8797 opentrace();
8798#endif
8799 setinteractive(iflag);
8800 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008801#if ENABLE_FEATURE_EDITING_VI
8802 if (viflag)
8803 line_input_state->flags |= VI_MODE;
8804 else
8805 line_input_state->flags &= ~VI_MODE;
8806#else
8807 viflag = 0; /* forcibly keep the option off */
8808#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008809}
8810
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008811static struct localvar *localvars;
8812
8813/*
8814 * Called after a function returns.
8815 * Interrupts must be off.
8816 */
8817static void
8818poplocalvars(void)
8819{
8820 struct localvar *lvp;
8821 struct var *vp;
8822
8823 while ((lvp = localvars) != NULL) {
8824 localvars = lvp->next;
8825 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008826 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008827 if (vp == NULL) { /* $- saved */
8828 memcpy(optlist, lvp->text, sizeof(optlist));
8829 free((char*)lvp->text);
8830 optschanged();
8831 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008832 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008833 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008834 if (vp->var_func)
8835 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008836 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008837 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008838 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008839 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008840 }
8841 free(lvp);
8842 }
8843}
8844
8845static int
8846evalfun(struct funcnode *func, int argc, char **argv, int flags)
8847{
8848 volatile struct shparam saveparam;
8849 struct localvar *volatile savelocalvars;
8850 struct jmploc *volatile savehandler;
8851 struct jmploc jmploc;
8852 int e;
8853
8854 saveparam = shellparam;
8855 savelocalvars = localvars;
8856 e = setjmp(jmploc.loc);
8857 if (e) {
8858 goto funcdone;
8859 }
8860 INT_OFF;
8861 savehandler = exception_handler;
8862 exception_handler = &jmploc;
8863 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008864 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008865 func->count++;
8866 funcnest++;
8867 INT_ON;
8868 shellparam.nparam = argc - 1;
8869 shellparam.p = argv + 1;
8870#if ENABLE_ASH_GETOPTS
8871 shellparam.optind = 1;
8872 shellparam.optoff = -1;
8873#endif
8874 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008875 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008876 INT_OFF;
8877 funcnest--;
8878 freefunc(func);
8879 poplocalvars();
8880 localvars = savelocalvars;
8881 freeparam(&shellparam);
8882 shellparam = saveparam;
8883 exception_handler = savehandler;
8884 INT_ON;
8885 evalskip &= ~SKIPFUNC;
8886 return e;
8887}
8888
Denis Vlasenko131ae172007-02-18 13:00:19 +00008889#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008890static char **
8891parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008892{
8893 char *cp, c;
8894
8895 for (;;) {
8896 cp = *++argv;
8897 if (!cp)
8898 return 0;
8899 if (*cp++ != '-')
8900 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008901 c = *cp++;
8902 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008903 break;
8904 if (c == '-' && !*cp) {
8905 argv++;
8906 break;
8907 }
8908 do {
8909 switch (c) {
8910 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008911 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008912 break;
8913 default:
8914 /* run 'typecmd' for other options */
8915 return 0;
8916 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008917 c = *cp++;
8918 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008919 }
8920 return argv;
8921}
8922#endif
8923
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008924/*
8925 * Make a variable a local variable. When a variable is made local, it's
8926 * value and flags are saved in a localvar structure. The saved values
8927 * will be restored when the shell function returns. We handle the name
8928 * "-" as a special case.
8929 */
8930static void
8931mklocal(char *name)
8932{
8933 struct localvar *lvp;
8934 struct var **vpp;
8935 struct var *vp;
8936
8937 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008938 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008939 if (LONE_DASH(name)) {
8940 char *p;
8941 p = ckmalloc(sizeof(optlist));
8942 lvp->text = memcpy(p, optlist, sizeof(optlist));
8943 vp = NULL;
8944 } else {
8945 char *eq;
8946
8947 vpp = hashvar(name);
8948 vp = *findvar(vpp, name);
8949 eq = strchr(name, '=');
8950 if (vp == NULL) {
8951 if (eq)
8952 setvareq(name, VSTRFIXED);
8953 else
8954 setvar(name, NULL, VSTRFIXED);
8955 vp = *vpp; /* the new variable */
8956 lvp->flags = VUNSET;
8957 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008958 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008959 lvp->flags = vp->flags;
8960 vp->flags |= VSTRFIXED|VTEXTFIXED;
8961 if (eq)
8962 setvareq(name, 0);
8963 }
8964 }
8965 lvp->vp = vp;
8966 lvp->next = localvars;
8967 localvars = lvp;
8968 INT_ON;
8969}
8970
8971/*
8972 * The "local" command.
8973 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008974static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008975localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008976{
8977 char *name;
8978
8979 argv = argptr;
8980 while ((name = *argv++) != NULL) {
8981 mklocal(name);
8982 }
8983 return 0;
8984}
8985
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008986static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008987falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008988{
8989 return 1;
8990}
8991
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008992static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008993truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008994{
8995 return 0;
8996}
8997
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008998static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008999execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009000{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009001 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009002 iflag = 0; /* exit on error */
9003 mflag = 0;
9004 optschanged();
9005 shellexec(argv + 1, pathval(), 0);
9006 }
9007 return 0;
9008}
9009
9010/*
9011 * The return command.
9012 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009013static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009014returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009015{
9016 /*
9017 * If called outside a function, do what ksh does;
9018 * skip the rest of the file.
9019 */
9020 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9021 return argv[1] ? number(argv[1]) : exitstatus;
9022}
9023
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009024/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009025static int breakcmd(int, char **) FAST_FUNC;
9026static int dotcmd(int, char **) FAST_FUNC;
9027static int evalcmd(int, char **) FAST_FUNC;
9028static int exitcmd(int, char **) FAST_FUNC;
9029static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009030#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009031static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009032#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009033#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009034static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009035#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009036#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009037static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009038#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009039static int readcmd(int, char **) FAST_FUNC;
9040static int setcmd(int, char **) FAST_FUNC;
9041static int shiftcmd(int, char **) FAST_FUNC;
9042static int timescmd(int, char **) FAST_FUNC;
9043static int trapcmd(int, char **) FAST_FUNC;
9044static int umaskcmd(int, char **) FAST_FUNC;
9045static int unsetcmd(int, char **) FAST_FUNC;
9046static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009047
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009048#define BUILTIN_NOSPEC "0"
9049#define BUILTIN_SPECIAL "1"
9050#define BUILTIN_REGULAR "2"
9051#define BUILTIN_SPEC_REG "3"
9052#define BUILTIN_ASSIGN "4"
9053#define BUILTIN_SPEC_ASSG "5"
9054#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009055#define BUILTIN_SPEC_REG_ASSG "7"
9056
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009057/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009058#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009059static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009060#endif
9061#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009062static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009063#endif
9064#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009065static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009066#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009067
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009068/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009069static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009070 { BUILTIN_SPEC_REG "." , dotcmd },
9071 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009074#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009075 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009076#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009077#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009078#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009079 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080#endif
9081#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009082 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009083#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009084 { BUILTIN_SPEC_REG "break" , breakcmd },
9085 { BUILTIN_REGULAR "cd" , cdcmd },
9086 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009087#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009088 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009089#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009090 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009091#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009092 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009093#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009094 { BUILTIN_SPEC_REG "eval" , evalcmd },
9095 { BUILTIN_SPEC_REG "exec" , execcmd },
9096 { BUILTIN_SPEC_REG "exit" , exitcmd },
9097 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9098 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009099#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009100 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009101#endif
9102#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009103 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009104#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009105 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009106#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009107 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009108#endif
9109#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009110 { BUILTIN_REGULAR "jobs" , jobscmd },
9111 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009112#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009113#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009114 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009115#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009116 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009117#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009119#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009120 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9121 { BUILTIN_REGULAR "read" , readcmd },
9122 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9123 { BUILTIN_SPEC_REG "return" , returncmd },
9124 { BUILTIN_SPEC_REG "set" , setcmd },
9125 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009126#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009127 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009128#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009129#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009130 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009131#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009132 { BUILTIN_SPEC_REG "times" , timescmd },
9133 { BUILTIN_SPEC_REG "trap" , trapcmd },
9134 { BUILTIN_REGULAR "true" , truecmd },
9135 { BUILTIN_NOSPEC "type" , typecmd },
9136 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9137 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009138#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009139 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009140#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009141 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9142 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009143};
9144
Denis Vlasenko80591b02008-03-25 07:49:43 +00009145/* Should match the above table! */
9146#define COMMANDCMD (builtintab + \
9147 2 + \
9148 1 * ENABLE_ASH_BUILTIN_TEST + \
9149 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9150 1 * ENABLE_ASH_ALIAS + \
9151 1 * ENABLE_ASH_JOB_CONTROL + \
9152 3)
9153#define EXECCMD (builtintab + \
9154 2 + \
9155 1 * ENABLE_ASH_BUILTIN_TEST + \
9156 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9157 1 * ENABLE_ASH_ALIAS + \
9158 1 * ENABLE_ASH_JOB_CONTROL + \
9159 3 + \
9160 1 * ENABLE_ASH_CMDCMD + \
9161 1 + \
9162 ENABLE_ASH_BUILTIN_ECHO + \
9163 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009164
9165/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009166 * Search the table of builtin commands.
9167 */
9168static struct builtincmd *
9169find_builtin(const char *name)
9170{
9171 struct builtincmd *bp;
9172
9173 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009174 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009175 pstrcmp
9176 );
9177 return bp;
9178}
9179
9180/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009181 * Execute a simple command.
9182 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009183static int
9184isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009185{
9186 const char *q = endofname(p);
9187 if (p == q)
9188 return 0;
9189 return *q == '=';
9190}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009191static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009192bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009193{
9194 /* Preserve exitstatus of a previous possible redirection
9195 * as POSIX mandates */
9196 return back_exitstatus;
9197}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009198static void
Eric Andersenc470f442003-07-28 09:56:35 +00009199evalcommand(union node *cmd, int flags)
9200{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009201 static const struct builtincmd null_bltin = {
9202 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009203 };
Eric Andersenc470f442003-07-28 09:56:35 +00009204 struct stackmark smark;
9205 union node *argp;
9206 struct arglist arglist;
9207 struct arglist varlist;
9208 char **argv;
9209 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009210 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009211 struct cmdentry cmdentry;
9212 struct job *jp;
9213 char *lastarg;
9214 const char *path;
9215 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009216 int status;
9217 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009218 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009219 smallint cmd_is_exec;
9220 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009221
9222 /* First expand the arguments. */
9223 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9224 setstackmark(&smark);
9225 back_exitstatus = 0;
9226
9227 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009228 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009229 varlist.lastp = &varlist.list;
9230 *varlist.lastp = NULL;
9231 arglist.lastp = &arglist.list;
9232 *arglist.lastp = NULL;
9233
9234 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009235 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009236 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9237 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9238 }
9239
Eric Andersenc470f442003-07-28 09:56:35 +00009240 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9241 struct strlist **spp;
9242
9243 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009244 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009245 expandarg(argp, &arglist, EXP_VARTILDE);
9246 else
9247 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9248
Eric Andersenc470f442003-07-28 09:56:35 +00009249 for (sp = *spp; sp; sp = sp->next)
9250 argc++;
9251 }
9252
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009253 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009254 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009255 TRACE(("evalcommand arg: %s\n", sp->text));
9256 *nargv++ = sp->text;
9257 }
9258 *nargv = NULL;
9259
9260 lastarg = NULL;
9261 if (iflag && funcnest == 0 && argc > 0)
9262 lastarg = nargv[-1];
9263
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009264 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009265 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009266 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009267
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009268 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009269 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9270 struct strlist **spp;
9271 char *p;
9272
9273 spp = varlist.lastp;
9274 expandarg(argp, &varlist, EXP_VARTILDE);
9275
9276 /*
9277 * Modify the command lookup path, if a PATH= assignment
9278 * is present
9279 */
9280 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009281 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009282 path = p;
9283 }
9284
9285 /* Print the command if xflag is set. */
9286 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009287 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009288 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009289
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009290 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009291 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009292 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009293 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009294 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009295 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009296 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009297 }
9298 sp = arglist.list;
9299 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009300 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009301 }
9302
9303 cmd_is_exec = 0;
9304 spclbltin = -1;
9305
9306 /* Now locate the command. */
9307 if (argc) {
Eric Andersenc470f442003-07-28 09:56:35 +00009308 int cmd_flag = DO_ERR;
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02009309#if ENABLE_ASH_CMDCMD
9310 const char *oldpath = path + 5;
9311#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009312 path += 5;
Eric Andersenc470f442003-07-28 09:56:35 +00009313 for (;;) {
9314 find_command(argv[0], &cmdentry, cmd_flag, path);
9315 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009316 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009317 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009318 goto bail;
9319 }
9320
9321 /* implement bltin and command here */
9322 if (cmdentry.cmdtype != CMDBUILTIN)
9323 break;
9324 if (spclbltin < 0)
9325 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9326 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009327 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009328#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009329 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009330 path = oldpath;
9331 nargv = parse_command_args(argv, &path);
9332 if (!nargv)
9333 break;
9334 argc -= nargv - argv;
9335 argv = nargv;
9336 cmd_flag |= DO_NOFUNC;
9337 } else
9338#endif
9339 break;
9340 }
9341 }
9342
9343 if (status) {
9344 /* We have a redirection error. */
9345 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009346 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009347 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009348 exitstatus = status;
9349 goto out;
9350 }
9351
9352 /* Execute the command. */
9353 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009354 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009355
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009356#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009357/* (1) BUG: if variables are set, we need to fork, or save/restore them
9358 * around run_nofork_applet() call.
9359 * (2) Should this check also be done in forkshell()?
9360 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9361 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009362 /* find_command() encodes applet_no as (-2 - applet_no) */
9363 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009364 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009365 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009366 /* run <applet>_main() */
9367 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009368 break;
9369 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009370#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009371 /* Can we avoid forking off? For example, very last command
9372 * in a script or a subshell does not need forking,
9373 * we can just exec it.
9374 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009375 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009376 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009377 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009378 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009379 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009380 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009381 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009382 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009383 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009384 break;
9385 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009386 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009387 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009388 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009389 }
9390 listsetvar(varlist.list, VEXPORT|VSTACK);
9391 shellexec(argv, path, cmdentry.u.index);
9392 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009393 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009394 case CMDBUILTIN:
9395 cmdenviron = varlist.list;
9396 if (cmdenviron) {
9397 struct strlist *list = cmdenviron;
9398 int i = VNOSET;
9399 if (spclbltin > 0 || argc == 0) {
9400 i = 0;
9401 if (cmd_is_exec && argc > 1)
9402 i = VEXPORT;
9403 }
9404 listsetvar(list, i);
9405 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009406 /* Tight loop with builtins only:
9407 * "while kill -0 $child; do true; done"
9408 * will never exit even if $child died, unless we do this
9409 * to reap the zombie and make kill detect that it's gone: */
9410 dowait(DOWAIT_NONBLOCK, NULL);
9411
Eric Andersenc470f442003-07-28 09:56:35 +00009412 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9413 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009414 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009415 if (i == EXEXIT)
9416 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009417 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009418 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009419 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009420 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009421 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009422 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009423 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009424 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009425 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009426 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009427 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009428 }
9429 break;
9430
9431 case CMDFUNCTION:
9432 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009433 /* See above for the rationale */
9434 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009435 if (evalfun(cmdentry.u.func, argc, argv, flags))
9436 goto raise;
9437 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009438
9439 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009440
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009441 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009442 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009443 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009444 /* dsl: I think this is intended to be used to support
9445 * '_' in 'vi' command mode during line editing...
9446 * However I implemented that within libedit itself.
9447 */
9448 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009449 }
Eric Andersenc470f442003-07-28 09:56:35 +00009450 popstackmark(&smark);
9451}
9452
9453static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009454evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9455{
Eric Andersenc470f442003-07-28 09:56:35 +00009456 char *volatile savecmdname;
9457 struct jmploc *volatile savehandler;
9458 struct jmploc jmploc;
9459 int i;
9460
9461 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009462 i = setjmp(jmploc.loc);
9463 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009464 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009465 savehandler = exception_handler;
9466 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009467 commandname = argv[0];
9468 argptr = argv + 1;
9469 optptr = NULL; /* initialize nextopt */
9470 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009471 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009472 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009473 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009474 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009475 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009476 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009477
9478 return i;
9479}
9480
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009481static int
9482goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009483{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009484 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009485}
9486
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009487
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009488/*
9489 * Search for a command. This is called before we fork so that the
9490 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009491 * the child. The check for "goodname" is an overly conservative
9492 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009493 */
Eric Andersenc470f442003-07-28 09:56:35 +00009494static void
9495prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009496{
9497 struct cmdentry entry;
9498
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009499 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9500 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009501}
9502
Eric Andersencb57d552001-06-28 07:25:16 +00009503
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009504/* ============ Builtin commands
9505 *
9506 * Builtin commands whose functions are closely tied to evaluation
9507 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009508 */
9509
9510/*
Eric Andersencb57d552001-06-28 07:25:16 +00009511 * Handle break and continue commands. Break, continue, and return are
9512 * all handled by setting the evalskip flag. The evaluation routines
9513 * above all check this flag, and if it is set they start skipping
9514 * commands rather than executing them. The variable skipcount is
9515 * the number of loops to break/continue, or the number of function
9516 * levels to return. (The latter is always 1.) It should probably
9517 * be an error to break out of more loops than exist, but it isn't
9518 * in the standard shell so we don't make it one here.
9519 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009520static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009521breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009522{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009523 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009524
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009525 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009526 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009527 if (n > loopnest)
9528 n = loopnest;
9529 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009530 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009531 skipcount = n;
9532 }
9533 return 0;
9534}
9535
Eric Andersenc470f442003-07-28 09:56:35 +00009536
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009537/* ============ input.c
9538 *
Eric Andersen90898442003-08-06 11:20:52 +00009539 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009540 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009541
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009542enum {
9543 INPUT_PUSH_FILE = 1,
9544 INPUT_NOFILE_OK = 2,
9545};
Eric Andersencb57d552001-06-28 07:25:16 +00009546
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009547static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009548/* values of checkkwd variable */
9549#define CHKALIAS 0x1
9550#define CHKKWD 0x2
9551#define CHKNL 0x4
9552
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009553/*
9554 * Push a string back onto the input at this current parsefile level.
9555 * We handle aliases this way.
9556 */
9557#if !ENABLE_ASH_ALIAS
9558#define pushstring(s, ap) pushstring(s)
9559#endif
9560static void
9561pushstring(char *s, struct alias *ap)
9562{
9563 struct strpush *sp;
9564 int len;
9565
9566 len = strlen(s);
9567 INT_OFF;
9568 if (g_parsefile->strpush) {
9569 sp = ckzalloc(sizeof(*sp));
9570 sp->prev = g_parsefile->strpush;
9571 } else {
9572 sp = &(g_parsefile->basestrpush);
9573 }
9574 g_parsefile->strpush = sp;
9575 sp->prev_string = g_parsefile->next_to_pgetc;
9576 sp->prev_left_in_line = g_parsefile->left_in_line;
9577#if ENABLE_ASH_ALIAS
9578 sp->ap = ap;
9579 if (ap) {
9580 ap->flag |= ALIASINUSE;
9581 sp->string = s;
9582 }
9583#endif
9584 g_parsefile->next_to_pgetc = s;
9585 g_parsefile->left_in_line = len;
9586 INT_ON;
9587}
9588
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009589static void
9590popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009591{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009592 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009593
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009594 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009595#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009596 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009597 if (g_parsefile->next_to_pgetc[-1] == ' '
9598 || g_parsefile->next_to_pgetc[-1] == '\t'
9599 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009600 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009601 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009602 if (sp->string != sp->ap->val) {
9603 free(sp->string);
9604 }
9605 sp->ap->flag &= ~ALIASINUSE;
9606 if (sp->ap->flag & ALIASDEAD) {
9607 unalias(sp->ap->name);
9608 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009609 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009610#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009611 g_parsefile->next_to_pgetc = sp->prev_string;
9612 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009613 g_parsefile->strpush = sp->prev;
9614 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009615 free(sp);
9616 INT_ON;
9617}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009618
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009619//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9620//it peeks whether it is &>, and then pushes back both chars.
9621//This function needs to save last *next_to_pgetc to buf[0]
9622//to make two pungetc() reliable. Currently,
9623// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009624static int
9625preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009626{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009627 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009628 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009629
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009630 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009631#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009632 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009633 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009634 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009635 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009636 int timeout = -1;
9637# if ENABLE_ASH_IDLE_TIMEOUT
9638 if (iflag) {
9639 const char *tmout_var = lookupvar("TMOUT");
9640 if (tmout_var) {
9641 timeout = atoi(tmout_var) * 1000;
9642 if (timeout <= 0)
9643 timeout = -1;
9644 }
9645 }
9646# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009647# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009648 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009649# endif
Denys Vlasenko20704f02011-03-23 17:59:27 +01009650 /* Unicode support should be activated even if LANG is set
9651 * _during_ shell execution, not only if it was set when
9652 * shell was started. Therefore, re-check LANG every time:
9653 */
9654 reinit_unicode(lookupvar("LANG"));
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009655 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009656 if (nr == 0) {
9657 /* Ctrl+C pressed */
9658 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009659 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009660 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009661 raise(SIGINT);
9662 return 1;
9663 }
Eric Andersenc470f442003-07-28 09:56:35 +00009664 goto retry;
9665 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009666 if (nr < 0) {
9667 if (errno == 0) {
9668 /* Ctrl+D pressed */
9669 nr = 0;
9670 }
9671# if ENABLE_ASH_IDLE_TIMEOUT
9672 else if (errno == EAGAIN && timeout > 0) {
9673 printf("\007timed out waiting for input: auto-logout\n");
9674 exitshell();
9675 }
9676# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009677 }
Eric Andersencb57d552001-06-28 07:25:16 +00009678 }
9679#else
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009680 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009681#endif
9682
Denys Vlasenko80c5b682011-05-08 21:21:10 +02009683#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009684 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009685 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009686 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009687 if (flags >= 0 && (flags & O_NONBLOCK)) {
9688 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009689 if (fcntl(0, F_SETFL, flags) >= 0) {
9690 out2str("sh: turning off NDELAY mode\n");
9691 goto retry;
9692 }
9693 }
9694 }
9695 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009696#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009697 return nr;
9698}
9699
9700/*
9701 * Refill the input buffer and return the next input character:
9702 *
9703 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009704 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9705 * or we are reading from a string so we can't refill the buffer,
9706 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009707 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009708 * 4) Process input up to the next newline, deleting nul characters.
9709 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009710//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9711#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009712static int
Eric Andersenc470f442003-07-28 09:56:35 +00009713preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009714{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009715 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009716 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009717
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009718 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009719#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009720 if (g_parsefile->left_in_line == -1
9721 && g_parsefile->strpush->ap
9722 && g_parsefile->next_to_pgetc[-1] != ' '
9723 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009724 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009725 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009726 return PEOA;
9727 }
Eric Andersen2870d962001-07-02 17:27:21 +00009728#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009729 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009730 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009731 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9732 g_parsefile->left_in_line,
9733 g_parsefile->next_to_pgetc,
9734 g_parsefile->next_to_pgetc);
9735 if (--g_parsefile->left_in_line >= 0)
9736 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009737 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009738 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009739 * "pgetc" needs refilling.
9740 */
9741
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009742 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009743 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009744 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009745 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009747 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009748 /* even in failure keep left_in_line and next_to_pgetc
9749 * in lock step, for correct multi-layer pungetc.
9750 * left_in_line was decremented before preadbuffer(),
9751 * must inc next_to_pgetc: */
9752 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009753 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009754 }
Eric Andersencb57d552001-06-28 07:25:16 +00009755
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009756 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009757 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009758 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009759 again:
9760 more = preadfd();
9761 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009762 /* don't try reading again */
9763 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009764 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009765 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009766 return PEOF;
9767 }
9768 }
9769
Denis Vlasenko727752d2008-11-28 03:41:47 +00009770 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009771 * Set g_parsefile->left_in_line
9772 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009773 * NUL chars are deleted.
9774 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009775 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009776 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009777 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009778
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009779 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009780
Denis Vlasenko727752d2008-11-28 03:41:47 +00009781 c = *q;
9782 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009783 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009784 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009785 q++;
9786 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009787 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009788 break;
9789 }
Eric Andersencb57d552001-06-28 07:25:16 +00009790 }
9791
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009792 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009793 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9794 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009795 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009796 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009797 }
9798 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009799 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009800
Eric Andersencb57d552001-06-28 07:25:16 +00009801 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009802 char save = *q;
9803 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009804 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009805 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009806 }
9807
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009808 pgetc_debug("preadbuffer at %d:%p'%s'",
9809 g_parsefile->left_in_line,
9810 g_parsefile->next_to_pgetc,
9811 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009812 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009813}
9814
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009815#define pgetc_as_macro() \
9816 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009817 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009818 : preadbuffer() \
9819 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009820
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009821static int
9822pgetc(void)
9823{
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 Vlasenko4d2183b2007-02-23 01:05:38 +00009828 return pgetc_as_macro();
9829}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009830
9831#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009832# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009833#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009834# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009835#endif
9836
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009837#if ENABLE_ASH_ALIAS
9838static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009839pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009840{
9841 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009842 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009843 pgetc_debug("pgetc_fast at %d:%p'%s'",
9844 g_parsefile->left_in_line,
9845 g_parsefile->next_to_pgetc,
9846 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009847 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009848 } while (c == PEOA);
9849 return c;
9850}
9851#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009852# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009853#endif
9854
9855/*
9856 * Read a line from the script.
9857 */
9858static char *
9859pfgets(char *line, int len)
9860{
9861 char *p = line;
9862 int nleft = len;
9863 int c;
9864
9865 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009866 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009867 if (c == PEOF) {
9868 if (p == line)
9869 return NULL;
9870 break;
9871 }
9872 *p++ = c;
9873 if (c == '\n')
9874 break;
9875 }
9876 *p = '\0';
9877 return line;
9878}
9879
Eric Andersenc470f442003-07-28 09:56:35 +00009880/*
9881 * Undo the last call to pgetc. Only one character may be pushed back.
9882 * PEOF may be pushed back.
9883 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009884static void
Eric Andersenc470f442003-07-28 09:56:35 +00009885pungetc(void)
9886{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009887 g_parsefile->left_in_line++;
9888 g_parsefile->next_to_pgetc--;
9889 pgetc_debug("pushed back to %d:%p'%s'",
9890 g_parsefile->left_in_line,
9891 g_parsefile->next_to_pgetc,
9892 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009893}
9894
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009895/*
9896 * To handle the "." command, a stack of input files is used. Pushfile
9897 * adds a new entry to the stack and popfile restores the previous level.
9898 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009899static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009900pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009901{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009902 struct parsefile *pf;
9903
Denis Vlasenko597906c2008-02-20 16:38:54 +00009904 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009905 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009906 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009907 /*pf->strpush = NULL; - ckzalloc did it */
9908 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009909 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009910}
9911
9912static void
9913popfile(void)
9914{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009915 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009916
Denis Vlasenkob012b102007-02-19 22:43:01 +00009917 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009918 if (pf->pf_fd >= 0)
9919 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009920 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009921 while (pf->strpush)
9922 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009923 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009924 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009925 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009926}
9927
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009928/*
9929 * Return to top level.
9930 */
9931static void
9932popallfiles(void)
9933{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009934 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009935 popfile();
9936}
9937
9938/*
9939 * Close the file(s) that the shell is reading commands from. Called
9940 * after a fork is done.
9941 */
9942static void
9943closescript(void)
9944{
9945 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009946 if (g_parsefile->pf_fd > 0) {
9947 close(g_parsefile->pf_fd);
9948 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009949 }
9950}
9951
9952/*
9953 * Like setinputfile, but takes an open file descriptor. Call this with
9954 * interrupts off.
9955 */
9956static void
9957setinputfd(int fd, int push)
9958{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009959 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009960 if (push) {
9961 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009962 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009963 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009964 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009965 if (g_parsefile->buf == NULL)
9966 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009967 g_parsefile->left_in_buffer = 0;
9968 g_parsefile->left_in_line = 0;
9969 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009970}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009971
Eric Andersenc470f442003-07-28 09:56:35 +00009972/*
9973 * Set the input to take input from a file. If push is set, push the
9974 * old input onto the stack first.
9975 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009976static int
9977setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009978{
9979 int fd;
9980 int fd2;
9981
Denis Vlasenkob012b102007-02-19 22:43:01 +00009982 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009983 fd = open(fname, O_RDONLY);
9984 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009985 if (flags & INPUT_NOFILE_OK)
9986 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009987 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009988 }
Eric Andersenc470f442003-07-28 09:56:35 +00009989 if (fd < 10) {
9990 fd2 = copyfd(fd, 10);
9991 close(fd);
9992 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009993 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009994 fd = fd2;
9995 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009996 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009997 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009998 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009999 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010000}
10001
Eric Andersencb57d552001-06-28 07:25:16 +000010002/*
10003 * Like setinputfile, but takes input from a string.
10004 */
Eric Andersenc470f442003-07-28 09:56:35 +000010005static void
10006setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000010007{
Denis Vlasenkob012b102007-02-19 22:43:01 +000010008 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010009 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010010 g_parsefile->next_to_pgetc = string;
10011 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010012 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010013 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010014 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010015}
10016
10017
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010018/* ============ mail.c
10019 *
10020 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010021 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010022
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010023#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010024
Eric Andersencb57d552001-06-28 07:25:16 +000010025#define MAXMBOXES 10
10026
Eric Andersenc470f442003-07-28 09:56:35 +000010027/* times of mailboxes */
10028static time_t mailtime[MAXMBOXES];
10029/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010030static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010031
Eric Andersencb57d552001-06-28 07:25:16 +000010032/*
Eric Andersenc470f442003-07-28 09:56:35 +000010033 * Print appropriate message(s) if mail has arrived.
10034 * If mail_var_path_changed is set,
10035 * then the value of MAIL has mail_var_path_changed,
10036 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010037 */
Eric Andersenc470f442003-07-28 09:56:35 +000010038static void
10039chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010040{
Eric Andersencb57d552001-06-28 07:25:16 +000010041 const char *mpath;
10042 char *p;
10043 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010044 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010045 struct stackmark smark;
10046 struct stat statb;
10047
Eric Andersencb57d552001-06-28 07:25:16 +000010048 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010049 mpath = mpathset() ? mpathval() : mailval();
10050 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010051 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010052 if (p == NULL)
10053 break;
10054 if (*p == '\0')
10055 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010056 for (q = p; *q; q++)
10057 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010058#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010059 if (q[-1] != '/')
10060 abort();
10061#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010062 q[-1] = '\0'; /* delete trailing '/' */
10063 if (stat(p, &statb) < 0) {
10064 *mtp = 0;
10065 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010066 }
Eric Andersenc470f442003-07-28 09:56:35 +000010067 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10068 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010069 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010070 pathopt ? pathopt : "you have mail"
10071 );
10072 }
10073 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010074 }
Eric Andersenc470f442003-07-28 09:56:35 +000010075 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010076 popstackmark(&smark);
10077}
Eric Andersencb57d552001-06-28 07:25:16 +000010078
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010079static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010080changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010081{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010082 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010083}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010084
Denis Vlasenko131ae172007-02-18 13:00:19 +000010085#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010086
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010087
10088/* ============ ??? */
10089
Eric Andersencb57d552001-06-28 07:25:16 +000010090/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010091 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010092 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010093static void
10094setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010095{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010096 char **newparam;
10097 char **ap;
10098 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010099
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010100 for (nparam = 0; argv[nparam]; nparam++)
10101 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010102 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10103 while (*argv) {
10104 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010105 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010106 *ap = NULL;
10107 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010108 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010109 shellparam.nparam = nparam;
10110 shellparam.p = newparam;
10111#if ENABLE_ASH_GETOPTS
10112 shellparam.optind = 1;
10113 shellparam.optoff = -1;
10114#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010115}
10116
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010117/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010118 * Process shell options. The global variable argptr contains a pointer
10119 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010120 *
10121 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10122 * For a non-interactive shell, an error condition encountered
10123 * by a special built-in ... shall cause the shell to write a diagnostic message
10124 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010125 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010126 * ...
10127 * Utility syntax error (option or operand error) Shall exit
10128 * ...
10129 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10130 * we see that bash does not do that (set "finishes" with error code 1 instead,
10131 * and shell continues), and people rely on this behavior!
10132 * Testcase:
10133 * set -o barfoo 2>/dev/null
10134 * echo $?
10135 *
10136 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010137 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010138static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010139plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010140{
10141 int i;
10142
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010143 if (name) {
10144 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010145 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010146 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010147 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010148 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010149 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010150 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010151 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010152 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010153 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010154 if (val) {
10155 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10156 } else {
10157 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10158 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010159 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010160 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010161}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010162static void
10163setoption(int flag, int val)
10164{
10165 int i;
10166
10167 for (i = 0; i < NOPTS; i++) {
10168 if (optletters(i) == flag) {
10169 optlist[i] = val;
10170 return;
10171 }
10172 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010173 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010174 /* NOTREACHED */
10175}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010176static int
Eric Andersenc470f442003-07-28 09:56:35 +000010177options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010178{
10179 char *p;
10180 int val;
10181 int c;
10182
10183 if (cmdline)
10184 minusc = NULL;
10185 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010186 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010187 if (c != '-' && c != '+')
10188 break;
10189 argptr++;
10190 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010191 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010192 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010193 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010194 if (!cmdline) {
10195 /* "-" means turn off -x and -v */
10196 if (p[0] == '\0')
10197 xflag = vflag = 0;
10198 /* "--" means reset params */
10199 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010200 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010201 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010202 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010203 }
Eric Andersencb57d552001-06-28 07:25:16 +000010204 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010205 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010206 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010207 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010208 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010209 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010210 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010211 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010212 /* it already printed err message */
10213 return 1; /* error */
10214 }
Eric Andersencb57d552001-06-28 07:25:16 +000010215 if (*argptr)
10216 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010217 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10218 isloginsh = 1;
10219 /* bash does not accept +-login, we also won't */
10220 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010221 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010222 isloginsh = 1;
10223 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010224 } else {
10225 setoption(c, val);
10226 }
10227 }
10228 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010229 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010230}
10231
Eric Andersencb57d552001-06-28 07:25:16 +000010232/*
Eric Andersencb57d552001-06-28 07:25:16 +000010233 * The shift builtin command.
10234 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010235static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010236shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010237{
10238 int n;
10239 char **ap1, **ap2;
10240
10241 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010242 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010243 n = number(argv[1]);
10244 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010245 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010246 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010247 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010248 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010249 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010250 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010251 }
10252 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010253 while ((*ap2++ = *ap1++) != NULL)
10254 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010255#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010256 shellparam.optind = 1;
10257 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010258#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010259 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010260 return 0;
10261}
10262
Eric Andersencb57d552001-06-28 07:25:16 +000010263/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010264 * POSIX requires that 'set' (but not export or readonly) output the
10265 * variables in lexicographic order - by the locale's collating order (sigh).
10266 * Maybe we could keep them in an ordered balanced binary tree
10267 * instead of hashed lists.
10268 * For now just roll 'em through qsort for printing...
10269 */
10270static int
10271showvars(const char *sep_prefix, int on, int off)
10272{
10273 const char *sep;
10274 char **ep, **epend;
10275
10276 ep = listvars(on, off, &epend);
10277 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10278
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010279 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010280
10281 for (; ep < epend; ep++) {
10282 const char *p;
10283 const char *q;
10284
10285 p = strchrnul(*ep, '=');
10286 q = nullstr;
10287 if (*p)
10288 q = single_quote(++p);
10289 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10290 }
10291 return 0;
10292}
10293
10294/*
Eric Andersencb57d552001-06-28 07:25:16 +000010295 * The set command builtin.
10296 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010297static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010298setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010299{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010300 int retval;
10301
Denis Vlasenko68404f12008-03-17 09:00:54 +000010302 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010303 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010304
Denis Vlasenkob012b102007-02-19 22:43:01 +000010305 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010306 retval = options(/*cmdline:*/ 0);
10307 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010308 optschanged();
10309 if (*argptr != NULL) {
10310 setparam(argptr);
10311 }
Eric Andersencb57d552001-06-28 07:25:16 +000010312 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010313 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010314 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010315}
10316
Denis Vlasenko131ae172007-02-18 13:00:19 +000010317#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010318static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010319change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010320{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010321 uint32_t t;
10322
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010323 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010324 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010325 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010326 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010327 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010328 vrandom.flags &= ~VNOFUNC;
10329 } else {
10330 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010331 t = strtoul(value, NULL, 10);
10332 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010333 }
Eric Andersenef02f822004-03-11 13:34:24 +000010334}
Eric Andersen16767e22004-03-16 05:14:10 +000010335#endif
10336
Denis Vlasenko131ae172007-02-18 13:00:19 +000010337#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010338static int
Eric Andersenc470f442003-07-28 09:56:35 +000010339getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010340{
10341 char *p, *q;
10342 char c = '?';
10343 int done = 0;
10344 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010345 char s[12];
10346 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010347
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010348 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010349 return 1;
10350 optnext = optfirst + *param_optind - 1;
10351
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010352 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010353 p = NULL;
10354 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010355 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010356 if (p == NULL || *p == '\0') {
10357 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010358 p = *optnext;
10359 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010360 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010361 p = NULL;
10362 done = 1;
10363 goto out;
10364 }
10365 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010366 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010367 goto atend;
10368 }
10369
10370 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010371 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010372 if (*q == '\0') {
10373 if (optstr[0] == ':') {
10374 s[0] = c;
10375 s[1] = '\0';
10376 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010377 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010378 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010379 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010380 }
10381 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010382 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010383 }
10384 if (*++q == ':')
10385 q++;
10386 }
10387
10388 if (*++q == ':') {
10389 if (*p == '\0' && (p = *optnext) == NULL) {
10390 if (optstr[0] == ':') {
10391 s[0] = c;
10392 s[1] = '\0';
10393 err |= setvarsafe("OPTARG", s, 0);
10394 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010395 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010396 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010397 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010398 c = '?';
10399 }
Eric Andersenc470f442003-07-28 09:56:35 +000010400 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010401 }
10402
10403 if (p == *optnext)
10404 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010405 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010406 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010407 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010408 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010409 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010410 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010411 *param_optind = optnext - optfirst + 1;
10412 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010413 err |= setvarsafe("OPTIND", s, VNOFUNC);
10414 s[0] = c;
10415 s[1] = '\0';
10416 err |= setvarsafe(optvar, s, 0);
10417 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010418 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010419 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010420 flush_stdout_stderr();
10421 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010422 }
10423 return done;
10424}
Eric Andersenc470f442003-07-28 09:56:35 +000010425
10426/*
10427 * The getopts builtin. Shellparam.optnext points to the next argument
10428 * to be processed. Shellparam.optptr points to the next character to
10429 * be processed in the current argument. If shellparam.optnext is NULL,
10430 * then it's the first time getopts has been called.
10431 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010432static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010433getoptscmd(int argc, char **argv)
10434{
10435 char **optbase;
10436
10437 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010438 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010439 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010440 optbase = shellparam.p;
10441 if (shellparam.optind > shellparam.nparam + 1) {
10442 shellparam.optind = 1;
10443 shellparam.optoff = -1;
10444 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010445 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010446 optbase = &argv[3];
10447 if (shellparam.optind > argc - 2) {
10448 shellparam.optind = 1;
10449 shellparam.optoff = -1;
10450 }
10451 }
10452
10453 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010454 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010455}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010456#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010457
Eric Andersencb57d552001-06-28 07:25:16 +000010458
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010459/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010460
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010461struct heredoc {
10462 struct heredoc *next; /* next here document in list */
10463 union node *here; /* redirection node */
10464 char *eofmark; /* string indicating end of input */
10465 smallint striptabs; /* if set, strip leading tabs */
10466};
10467
10468static smallint tokpushback; /* last token pushed back */
10469static smallint parsebackquote; /* nonzero if we are inside backquotes */
10470static smallint quoteflag; /* set if (part of) last token was quoted */
10471static token_id_t lasttoken; /* last token read (integer id Txxx) */
10472static struct heredoc *heredoclist; /* list of here documents to read */
10473static char *wordtext; /* text of last word returned by readtoken */
10474static struct nodelist *backquotelist;
10475static union node *redirnode;
10476static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010477
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010478static const char *
10479tokname(char *buf, int tok)
10480{
10481 if (tok < TSEMI)
10482 return tokname_array[tok] + 1;
10483 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10484 return buf;
10485}
10486
10487/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010488 * Called when an unexpected token is read during the parse. The argument
10489 * is the token that is expected, or -1 if more than one type of token can
10490 * occur at this point.
10491 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010492static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010493static void
10494raise_error_unexpected_syntax(int token)
10495{
10496 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010497 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010498 int l;
10499
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010500 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010501 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010502 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010503 raise_error_syntax(msg);
10504 /* NOTREACHED */
10505}
Eric Andersencb57d552001-06-28 07:25:16 +000010506
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010507#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010508
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010509/* parsing is heavily cross-recursive, need these forward decls */
10510static union node *andor(void);
10511static union node *pipeline(void);
10512static union node *parse_command(void);
10513static void parseheredoc(void);
10514static char peektoken(void);
10515static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010516
Eric Andersenc470f442003-07-28 09:56:35 +000010517static union node *
10518list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010519{
10520 union node *n1, *n2, *n3;
10521 int tok;
10522
Eric Andersenc470f442003-07-28 09:56:35 +000010523 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10524 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010525 return NULL;
10526 n1 = NULL;
10527 for (;;) {
10528 n2 = andor();
10529 tok = readtoken();
10530 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010531 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010532 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010533 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010534 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010535 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010536 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010537 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010538 n2 = n3;
10539 }
10540 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 }
10542 }
10543 if (n1 == NULL) {
10544 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010545 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010546 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010547 n3->type = NSEMI;
10548 n3->nbinary.ch1 = n1;
10549 n3->nbinary.ch2 = n2;
10550 n1 = n3;
10551 }
10552 switch (tok) {
10553 case TBACKGND:
10554 case TSEMI:
10555 tok = readtoken();
10556 /* fall through */
10557 case TNL:
10558 if (tok == TNL) {
10559 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010560 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010561 return n1;
10562 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010563 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010564 }
Eric Andersenc470f442003-07-28 09:56:35 +000010565 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010566 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010567 return n1;
10568 break;
10569 case TEOF:
10570 if (heredoclist)
10571 parseheredoc();
10572 else
Eric Andersenc470f442003-07-28 09:56:35 +000010573 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010574 return n1;
10575 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010576 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010577 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010578 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010579 return n1;
10580 }
10581 }
10582}
10583
Eric Andersenc470f442003-07-28 09:56:35 +000010584static union node *
10585andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010586{
Eric Andersencb57d552001-06-28 07:25:16 +000010587 union node *n1, *n2, *n3;
10588 int t;
10589
Eric Andersencb57d552001-06-28 07:25:16 +000010590 n1 = pipeline();
10591 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010592 t = readtoken();
10593 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010594 t = NAND;
10595 } else if (t == TOR) {
10596 t = NOR;
10597 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010598 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010599 return n1;
10600 }
Eric Andersenc470f442003-07-28 09:56:35 +000010601 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010602 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010603 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010604 n3->type = t;
10605 n3->nbinary.ch1 = n1;
10606 n3->nbinary.ch2 = n2;
10607 n1 = n3;
10608 }
10609}
10610
Eric Andersenc470f442003-07-28 09:56:35 +000010611static union node *
10612pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010613{
Eric Andersencb57d552001-06-28 07:25:16 +000010614 union node *n1, *n2, *pipenode;
10615 struct nodelist *lp, *prev;
10616 int negate;
10617
10618 negate = 0;
10619 TRACE(("pipeline: entered\n"));
10620 if (readtoken() == TNOT) {
10621 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010622 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010623 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010624 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010625 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010626 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010627 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010628 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010629 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010630 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010631 pipenode->npipe.cmdlist = lp;
10632 lp->n = n1;
10633 do {
10634 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010635 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010636 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010637 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010638 prev->next = lp;
10639 } while (readtoken() == TPIPE);
10640 lp->next = NULL;
10641 n1 = pipenode;
10642 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010643 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010644 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010645 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010646 n2->type = NNOT;
10647 n2->nnot.com = n1;
10648 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010649 }
10650 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010651}
10652
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010653static union node *
10654makename(void)
10655{
10656 union node *n;
10657
Denis Vlasenko597906c2008-02-20 16:38:54 +000010658 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010659 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010660 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010661 n->narg.text = wordtext;
10662 n->narg.backquote = backquotelist;
10663 return n;
10664}
10665
10666static void
10667fixredir(union node *n, const char *text, int err)
10668{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010669 int fd;
10670
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010671 TRACE(("Fix redir %s %d\n", text, err));
10672 if (!err)
10673 n->ndup.vname = NULL;
10674
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010675 fd = bb_strtou(text, NULL, 10);
10676 if (!errno && fd >= 0)
10677 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010678 else if (LONE_DASH(text))
10679 n->ndup.dupfd = -1;
10680 else {
10681 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010682 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010683 n->ndup.vname = makename();
10684 }
10685}
10686
10687/*
10688 * Returns true if the text contains nothing to expand (no dollar signs
10689 * or backquotes).
10690 */
10691static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010692noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010693{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010694 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010695
Denys Vlasenkocd716832009-11-28 22:14:02 +010010696 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010697 if (c == CTLQUOTEMARK)
10698 continue;
10699 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010700 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010701 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010702 return 0;
10703 }
10704 return 1;
10705}
10706
10707static void
10708parsefname(void)
10709{
10710 union node *n = redirnode;
10711
10712 if (readtoken() != TWORD)
10713 raise_error_unexpected_syntax(-1);
10714 if (n->type == NHERE) {
10715 struct heredoc *here = heredoc;
10716 struct heredoc *p;
10717 int i;
10718
10719 if (quoteflag == 0)
10720 n->type = NXHERE;
10721 TRACE(("Here document %d\n", n->type));
10722 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010723 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010724 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010725 here->eofmark = wordtext;
10726 here->next = NULL;
10727 if (heredoclist == NULL)
10728 heredoclist = here;
10729 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010730 for (p = heredoclist; p->next; p = p->next)
10731 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010732 p->next = here;
10733 }
10734 } else if (n->type == NTOFD || n->type == NFROMFD) {
10735 fixredir(n, wordtext, 0);
10736 } else {
10737 n->nfile.fname = makename();
10738 }
10739}
Eric Andersencb57d552001-06-28 07:25:16 +000010740
Eric Andersenc470f442003-07-28 09:56:35 +000010741static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010742simplecmd(void)
10743{
10744 union node *args, **app;
10745 union node *n = NULL;
10746 union node *vars, **vpp;
10747 union node **rpp, *redir;
10748 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010749#if ENABLE_ASH_BASH_COMPAT
10750 smallint double_brackets_flag = 0;
10751#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010752
10753 args = NULL;
10754 app = &args;
10755 vars = NULL;
10756 vpp = &vars;
10757 redir = NULL;
10758 rpp = &redir;
10759
10760 savecheckkwd = CHKALIAS;
10761 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010762 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010763 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010764 t = readtoken();
10765 switch (t) {
10766#if ENABLE_ASH_BASH_COMPAT
10767 case TAND: /* "&&" */
10768 case TOR: /* "||" */
10769 if (!double_brackets_flag) {
10770 tokpushback = 1;
10771 goto out;
10772 }
10773 wordtext = (char *) (t == TAND ? "-a" : "-o");
10774#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010775 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010776 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010777 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010778 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010779 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010780#if ENABLE_ASH_BASH_COMPAT
10781 if (strcmp("[[", wordtext) == 0)
10782 double_brackets_flag = 1;
10783 else if (strcmp("]]", wordtext) == 0)
10784 double_brackets_flag = 0;
10785#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010786 n->narg.backquote = backquotelist;
10787 if (savecheckkwd && isassignment(wordtext)) {
10788 *vpp = n;
10789 vpp = &n->narg.next;
10790 } else {
10791 *app = n;
10792 app = &n->narg.next;
10793 savecheckkwd = 0;
10794 }
10795 break;
10796 case TREDIR:
10797 *rpp = n = redirnode;
10798 rpp = &n->nfile.next;
10799 parsefname(); /* read name of redirection file */
10800 break;
10801 case TLP:
10802 if (args && app == &args->narg.next
10803 && !vars && !redir
10804 ) {
10805 struct builtincmd *bcmd;
10806 const char *name;
10807
10808 /* We have a function */
10809 if (readtoken() != TRP)
10810 raise_error_unexpected_syntax(TRP);
10811 name = n->narg.text;
10812 if (!goodname(name)
10813 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10814 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010815 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010816 }
10817 n->type = NDEFUN;
10818 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10819 n->narg.next = parse_command();
10820 return n;
10821 }
10822 /* fall through */
10823 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010824 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010825 goto out;
10826 }
10827 }
10828 out:
10829 *app = NULL;
10830 *vpp = NULL;
10831 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010832 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010833 n->type = NCMD;
10834 n->ncmd.args = args;
10835 n->ncmd.assign = vars;
10836 n->ncmd.redirect = redir;
10837 return n;
10838}
10839
10840static union node *
10841parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010842{
Eric Andersencb57d552001-06-28 07:25:16 +000010843 union node *n1, *n2;
10844 union node *ap, **app;
10845 union node *cp, **cpp;
10846 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010847 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010848 int t;
10849
10850 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010851 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010852
Eric Andersencb57d552001-06-28 07:25:16 +000010853 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010854 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010855 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010856 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010857 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010858 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010859 n1->type = NIF;
10860 n1->nif.test = list(0);
10861 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010862 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010863 n1->nif.ifpart = list(0);
10864 n2 = n1;
10865 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010866 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010867 n2 = n2->nif.elsepart;
10868 n2->type = NIF;
10869 n2->nif.test = list(0);
10870 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010871 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010872 n2->nif.ifpart = list(0);
10873 }
10874 if (lasttoken == TELSE)
10875 n2->nif.elsepart = list(0);
10876 else {
10877 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010878 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010879 }
Eric Andersenc470f442003-07-28 09:56:35 +000010880 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010881 break;
10882 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010883 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010884 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010885 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010886 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010887 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010888 got = readtoken();
10889 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010890 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010891 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010892 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010893 }
10894 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010895 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010896 break;
10897 }
10898 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010899 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010900 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010901 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010902 n1->type = NFOR;
10903 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010904 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010905 if (readtoken() == TIN) {
10906 app = &ap;
10907 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010908 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010909 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010910 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010911 n2->narg.text = wordtext;
10912 n2->narg.backquote = backquotelist;
10913 *app = n2;
10914 app = &n2->narg.next;
10915 }
10916 *app = NULL;
10917 n1->nfor.args = ap;
10918 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010919 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010920 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010921 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010922 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010923 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010924 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010925 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010926 n1->nfor.args = n2;
10927 /*
10928 * Newline or semicolon here is optional (but note
10929 * that the original Bourne shell only allowed NL).
10930 */
10931 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010932 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010933 }
Eric Andersenc470f442003-07-28 09:56:35 +000010934 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010935 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010936 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010937 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010938 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010939 break;
10940 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010941 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010942 n1->type = NCASE;
10943 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010944 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010945 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010946 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010947 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010948 n2->narg.text = wordtext;
10949 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010950 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010951 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010952 } while (readtoken() == TNL);
10953 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010954 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010955 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010956 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010957 checkkwd = CHKNL | CHKKWD;
10958 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010959 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010960 if (lasttoken == TLP)
10961 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010962 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010963 cp->type = NCLIST;
10964 app = &cp->nclist.pattern;
10965 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010966 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010967 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010968 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010969 ap->narg.text = wordtext;
10970 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010971 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010972 break;
10973 app = &ap->narg.next;
10974 readtoken();
10975 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010976 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010977 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010978 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010979 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010980
Eric Andersenc470f442003-07-28 09:56:35 +000010981 cpp = &cp->nclist.next;
10982
10983 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010984 t = readtoken();
10985 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010986 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010987 raise_error_unexpected_syntax(TENDCASE);
10988 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010989 }
Eric Andersenc470f442003-07-28 09:56:35 +000010990 }
Eric Andersencb57d552001-06-28 07:25:16 +000010991 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010992 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010993 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010994 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010995 n1->type = NSUBSHELL;
10996 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010997 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010998 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010999 break;
11000 case TBEGIN:
11001 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011002 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000011003 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011004 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000011005 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011006 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011007 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000011008 }
11009
Eric Andersenc470f442003-07-28 09:56:35 +000011010 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011011 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000011012
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011013 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000011014 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000011015 checkkwd = CHKKWD | CHKALIAS;
11016 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011017 while (readtoken() == TREDIR) {
11018 *rpp = n2 = redirnode;
11019 rpp = &n2->nfile.next;
11020 parsefname();
11021 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011022 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011023 *rpp = NULL;
11024 if (redir) {
11025 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011026 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011027 n2->type = NREDIR;
11028 n2->nredir.n = n1;
11029 n1 = n2;
11030 }
11031 n1->nredir.redirect = redir;
11032 }
Eric Andersencb57d552001-06-28 07:25:16 +000011033 return n1;
11034}
11035
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011036#if ENABLE_ASH_BASH_COMPAT
11037static int decode_dollar_squote(void)
11038{
11039 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11040 int c, cnt;
11041 char *p;
11042 char buf[4];
11043
11044 c = pgetc();
11045 p = strchr(C_escapes, c);
11046 if (p) {
11047 buf[0] = c;
11048 p = buf;
11049 cnt = 3;
11050 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11051 do {
11052 c = pgetc();
11053 *++p = c;
11054 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11055 pungetc();
11056 } else if (c == 'x') { /* \xHH */
11057 do {
11058 c = pgetc();
11059 *++p = c;
11060 } while (isxdigit(c) && --cnt);
11061 pungetc();
11062 if (cnt == 3) { /* \x but next char is "bad" */
11063 c = 'x';
11064 goto unrecognized;
11065 }
11066 } else { /* simple seq like \\ or \t */
11067 p++;
11068 }
11069 *p = '\0';
11070 p = buf;
11071 c = bb_process_escape_sequence((void*)&p);
11072 } else { /* unrecognized "\z": print both chars unless ' or " */
11073 if (c != '\'' && c != '"') {
11074 unrecognized:
11075 c |= 0x100; /* "please encode \, then me" */
11076 }
11077 }
11078 return c;
11079}
11080#endif
11081
Eric Andersencb57d552001-06-28 07:25:16 +000011082/*
11083 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11084 * is not NULL, read a here document. In the latter case, eofmark is the
11085 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011086 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011087 * is the first character of the input token or document.
11088 *
11089 * Because C does not have internal subroutines, I have simulated them
11090 * using goto's to implement the subroutine linkage. The following macros
11091 * will run code that appears at the end of readtoken1.
11092 */
Eric Andersen2870d962001-07-02 17:27:21 +000011093#define CHECKEND() {goto checkend; checkend_return:;}
11094#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11095#define PARSESUB() {goto parsesub; parsesub_return:;}
11096#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11097#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11098#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011099static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011100readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011101{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011102 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011103 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011104 char *out;
11105 int len;
11106 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011107 struct nodelist *bqlist;
11108 smallint quotef;
11109 smallint dblquote;
11110 smallint oldstyle;
11111 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011112#if ENABLE_ASH_EXPAND_PRMT
11113 smallint pssyntax; /* we are expanding a prompt string */
11114#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011115 int varnest; /* levels of variables expansion */
11116 int arinest; /* levels of arithmetic expansion */
11117 int parenlevel; /* levels of parens in arithmetic */
11118 int dqvarnest; /* levels of variables expansion within double quotes */
11119
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011120 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011121
Eric Andersencb57d552001-06-28 07:25:16 +000011122#if __GNUC__
11123 /* Avoid longjmp clobbering */
11124 (void) &out;
11125 (void) &quotef;
11126 (void) &dblquote;
11127 (void) &varnest;
11128 (void) &arinest;
11129 (void) &parenlevel;
11130 (void) &dqvarnest;
11131 (void) &oldstyle;
11132 (void) &prevsyntax;
11133 (void) &syntax;
11134#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011135 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011136 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011137 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011138 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011139#if ENABLE_ASH_EXPAND_PRMT
11140 pssyntax = (syntax == PSSYNTAX);
11141 if (pssyntax)
11142 syntax = DQSYNTAX;
11143#endif
11144 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011145 varnest = 0;
11146 arinest = 0;
11147 parenlevel = 0;
11148 dqvarnest = 0;
11149
11150 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011151 loop:
11152 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011153 CHECKEND(); /* set c to PEOF if at end of here document */
11154 for (;;) { /* until end of line or end of word */
11155 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11156 switch (SIT(c, syntax)) {
11157 case CNL: /* '\n' */
11158 if (syntax == BASESYNTAX)
11159 goto endword; /* exit outer loop */
11160 USTPUTC(c, out);
11161 g_parsefile->linno++;
11162 setprompt_if(doprompt, 2);
11163 c = pgetc();
11164 goto loop; /* continue outer loop */
11165 case CWORD:
11166 USTPUTC(c, out);
11167 break;
11168 case CCTL:
11169 if (eofmark == NULL || dblquote)
11170 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011171#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011172 if (c == '\\' && bash_dollar_squote) {
11173 c = decode_dollar_squote();
11174 if (c & 0x100) {
11175 USTPUTC('\\', out);
11176 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011177 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011178 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011179#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011180 USTPUTC(c, out);
11181 break;
11182 case CBACK: /* backslash */
11183 c = pgetc_without_PEOA();
11184 if (c == PEOF) {
11185 USTPUTC(CTLESC, out);
11186 USTPUTC('\\', out);
11187 pungetc();
11188 } else if (c == '\n') {
11189 setprompt_if(doprompt, 2);
11190 } else {
11191#if ENABLE_ASH_EXPAND_PRMT
11192 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011193 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011194 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011195 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011196#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011197 /* Backslash is retained if we are in "str" and next char isn't special */
11198 if (dblquote
11199 && c != '\\'
11200 && c != '`'
11201 && c != '$'
11202 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011203 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011204 USTPUTC(CTLESC, out);
11205 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011206 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011207 if (SIT(c, SQSYNTAX) == CCTL)
11208 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011209 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011210 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011211 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011212 break;
11213 case CSQUOTE:
11214 syntax = SQSYNTAX;
11215 quotemark:
11216 if (eofmark == NULL) {
11217 USTPUTC(CTLQUOTEMARK, out);
11218 }
11219 break;
11220 case CDQUOTE:
11221 syntax = DQSYNTAX;
11222 dblquote = 1;
11223 goto quotemark;
11224 case CENDQUOTE:
11225 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11226 if (eofmark != NULL && arinest == 0
11227 && varnest == 0
11228 ) {
11229 USTPUTC(c, out);
11230 } else {
11231 if (dqvarnest == 0) {
11232 syntax = BASESYNTAX;
11233 dblquote = 0;
11234 }
11235 quotef = 1;
11236 goto quotemark;
11237 }
11238 break;
11239 case CVAR: /* '$' */
11240 PARSESUB(); /* parse substitution */
11241 break;
11242 case CENDVAR: /* '}' */
11243 if (varnest > 0) {
11244 varnest--;
11245 if (dqvarnest > 0) {
11246 dqvarnest--;
11247 }
11248 c = CTLENDVAR;
11249 }
11250 USTPUTC(c, out);
11251 break;
11252#if ENABLE_SH_MATH_SUPPORT
11253 case CLP: /* '(' in arithmetic */
11254 parenlevel++;
11255 USTPUTC(c, out);
11256 break;
11257 case CRP: /* ')' in arithmetic */
11258 if (parenlevel > 0) {
11259 parenlevel--;
11260 } else {
11261 if (pgetc() == ')') {
11262 if (--arinest == 0) {
11263 syntax = prevsyntax;
11264 dblquote = (syntax == DQSYNTAX);
11265 c = CTLENDARI;
11266 }
11267 } else {
11268 /*
11269 * unbalanced parens
11270 * (don't 2nd guess - no error)
11271 */
11272 pungetc();
11273 }
11274 }
11275 USTPUTC(c, out);
11276 break;
11277#endif
11278 case CBQUOTE: /* '`' */
11279 PARSEBACKQOLD();
11280 break;
11281 case CENDFILE:
11282 goto endword; /* exit outer loop */
11283 case CIGN:
11284 break;
11285 default:
11286 if (varnest == 0) {
11287#if ENABLE_ASH_BASH_COMPAT
11288 if (c == '&') {
11289 if (pgetc() == '>')
11290 c = 0x100 + '>'; /* flag &> */
11291 pungetc();
11292 }
11293#endif
11294 goto endword; /* exit outer loop */
11295 }
11296 IF_ASH_ALIAS(if (c != PEOA))
11297 USTPUTC(c, out);
11298 }
11299 c = pgetc_fast();
11300 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011301 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011302
Mike Frysinger98c52642009-04-02 10:02:37 +000011303#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011304 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011305 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011306#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011307 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011308 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011309 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011310 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011311 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011312 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011313 }
11314 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011315 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011316 out = stackblock();
11317 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011318 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011319 && quotef == 0
11320 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011321 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011322 PARSEREDIR(); /* passed as params: out, c */
11323 lasttoken = TREDIR;
11324 return lasttoken;
11325 }
11326 /* else: non-number X seen, interpret it
11327 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011328 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011329 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011330 }
11331 quoteflag = quotef;
11332 backquotelist = bqlist;
11333 grabstackblock(len);
11334 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011335 lasttoken = TWORD;
11336 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011337/* end of readtoken routine */
11338
Eric Andersencb57d552001-06-28 07:25:16 +000011339/*
11340 * Check to see whether we are at the end of the here document. When this
11341 * is called, c is set to the first character of the next input line. If
11342 * we are at the end of the here document, this routine sets the c to PEOF.
11343 */
Eric Andersenc470f442003-07-28 09:56:35 +000011344checkend: {
11345 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011346#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011347 if (c == PEOA)
11348 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011349#endif
11350 if (striptabs) {
11351 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011352 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011353 }
Eric Andersenc470f442003-07-28 09:56:35 +000011354 }
11355 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011356 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011357 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011358
Eric Andersenc470f442003-07-28 09:56:35 +000011359 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011360 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11361 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011362 if (*p == '\n' && *q == '\0') {
11363 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011364 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011365 needprompt = doprompt;
11366 } else {
11367 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011368 }
11369 }
11370 }
11371 }
Eric Andersenc470f442003-07-28 09:56:35 +000011372 goto checkend_return;
11373}
Eric Andersencb57d552001-06-28 07:25:16 +000011374
Eric Andersencb57d552001-06-28 07:25:16 +000011375/*
11376 * Parse a redirection operator. The variable "out" points to a string
11377 * specifying the fd to be redirected. The variable "c" contains the
11378 * first character of the redirection operator.
11379 */
Eric Andersenc470f442003-07-28 09:56:35 +000011380parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011381 /* out is already checked to be a valid number or "" */
11382 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011383 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011384
Denis Vlasenko597906c2008-02-20 16:38:54 +000011385 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011386 if (c == '>') {
11387 np->nfile.fd = 1;
11388 c = pgetc();
11389 if (c == '>')
11390 np->type = NAPPEND;
11391 else if (c == '|')
11392 np->type = NCLOBBER;
11393 else if (c == '&')
11394 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011395 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011396 else {
11397 np->type = NTO;
11398 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011399 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011400 }
11401#if ENABLE_ASH_BASH_COMPAT
11402 else if (c == 0x100 + '>') { /* this flags &> redirection */
11403 np->nfile.fd = 1;
11404 pgetc(); /* this is '>', no need to check */
11405 np->type = NTO2;
11406 }
11407#endif
11408 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011409 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011410 c = pgetc();
11411 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011412 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011413 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011414 np = stzalloc(sizeof(struct nhere));
11415 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011416 }
11417 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011418 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011419 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011420 c = pgetc();
11421 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011422 heredoc->striptabs = 1;
11423 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011424 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011425 pungetc();
11426 }
11427 break;
11428
11429 case '&':
11430 np->type = NFROMFD;
11431 break;
11432
11433 case '>':
11434 np->type = NFROMTO;
11435 break;
11436
11437 default:
11438 np->type = NFROM;
11439 pungetc();
11440 break;
11441 }
Eric Andersencb57d552001-06-28 07:25:16 +000011442 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011443 if (fd >= 0)
11444 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011445 redirnode = np;
11446 goto parseredir_return;
11447}
Eric Andersencb57d552001-06-28 07:25:16 +000011448
Eric Andersencb57d552001-06-28 07:25:16 +000011449/*
11450 * Parse a substitution. At this point, we have read the dollar sign
11451 * and nothing else.
11452 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011453
11454/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11455 * (assuming ascii char codes, as the original implementation did) */
11456#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011457 (((unsigned)(c) - 33 < 32) \
11458 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011459parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011460 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011461 int typeloc;
11462 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011463
Eric Andersenc470f442003-07-28 09:56:35 +000011464 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011465 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011466 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011467 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011468#if ENABLE_ASH_BASH_COMPAT
11469 if (c == '\'')
11470 bash_dollar_squote = 1;
11471 else
11472#endif
11473 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011474 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011475 } else if (c == '(') {
11476 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011477 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011478#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011479 PARSEARITH();
11480#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011481 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011482#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011483 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011484 pungetc();
11485 PARSEBACKQNEW();
11486 }
11487 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011488 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011489 USTPUTC(CTLVAR, out);
11490 typeloc = out - (char *)stackblock();
11491 USTPUTC(VSNORMAL, out);
11492 subtype = VSNORMAL;
11493 if (c == '{') {
11494 c = pgetc();
11495 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011496 c = pgetc();
11497 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011498 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011499 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011500 subtype = VSLENGTH; /* ${#VAR} */
11501 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011502 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011503 }
Eric Andersenc470f442003-07-28 09:56:35 +000011504 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011505 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011506 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011507 do {
11508 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011509 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011510 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011511 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011512 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011513 do {
11514 STPUTC(c, out);
11515 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011516 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011517 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011518 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011519 USTPUTC(c, out);
11520 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011521 } else {
11522 badsub:
11523 raise_error_syntax("bad substitution");
11524 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011525 if (c != '}' && subtype == VSLENGTH) {
11526 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011527 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011528 }
Eric Andersencb57d552001-06-28 07:25:16 +000011529
Eric Andersenc470f442003-07-28 09:56:35 +000011530 STPUTC('=', out);
11531 flags = 0;
11532 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011533 /* ${VAR...} but not $VAR or ${#VAR} */
11534 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011535 switch (c) {
11536 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011537 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011538#if ENABLE_ASH_BASH_COMPAT
11539 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011540//TODO: support more general format ${v:EXPR:EXPR},
11541// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011542 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011543 pungetc();
11544 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011545 }
11546#endif
11547 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011548 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011549 default: {
11550 static const char types[] ALIGN1 = "}-+?=";
11551 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011552 if (p == NULL)
11553 goto badsub;
11554 subtype = p - types + VSNORMAL;
11555 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011556 }
Eric Andersenc470f442003-07-28 09:56:35 +000011557 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011558 case '#': {
11559 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011560 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011561 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011562 if (c != cc)
11563 goto do_pungetc;
11564 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011565 break;
11566 }
11567#if ENABLE_ASH_BASH_COMPAT
11568 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011569 /* ${v/[/]pattern/repl} */
11570//TODO: encode pattern and repl separately.
11571// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011572 subtype = VSREPLACE;
11573 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011574 if (c != '/')
11575 goto do_pungetc;
11576 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011577 break;
11578#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011579 }
Eric Andersenc470f442003-07-28 09:56:35 +000011580 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011581 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011582 pungetc();
11583 }
11584 if (dblquote || arinest)
11585 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011586 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011587 if (subtype != VSNORMAL) {
11588 varnest++;
11589 if (dblquote || arinest) {
11590 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011591 }
11592 }
11593 }
Eric Andersenc470f442003-07-28 09:56:35 +000011594 goto parsesub_return;
11595}
Eric Andersencb57d552001-06-28 07:25:16 +000011596
Eric Andersencb57d552001-06-28 07:25:16 +000011597/*
11598 * Called to parse command substitutions. Newstyle is set if the command
11599 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11600 * list of commands (passed by reference), and savelen is the number of
11601 * characters on the top of the stack which must be preserved.
11602 */
Eric Andersenc470f442003-07-28 09:56:35 +000011603parsebackq: {
11604 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011605 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011606 union node *n;
11607 char *volatile str;
11608 struct jmploc jmploc;
11609 struct jmploc *volatile savehandler;
11610 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011611 smallint saveprompt = 0;
11612
Eric Andersencb57d552001-06-28 07:25:16 +000011613#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011614 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011615#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011616 savepbq = parsebackquote;
11617 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011618 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011619 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011620 exception_handler = savehandler;
11621 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011622 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011623 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011624 str = NULL;
11625 savelen = out - (char *)stackblock();
11626 if (savelen > 0) {
11627 str = ckmalloc(savelen);
11628 memcpy(str, stackblock(), savelen);
11629 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011630 savehandler = exception_handler;
11631 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011632 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011633 if (oldstyle) {
11634 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010011635 * treatment to some slashes, and then push the string and
11636 * reread it as input, interpreting it normally.
11637 */
Eric Andersenc470f442003-07-28 09:56:35 +000011638 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011639 size_t psavelen;
11640 char *pstr;
11641
Eric Andersenc470f442003-07-28 09:56:35 +000011642 STARTSTACKSTR(pout);
11643 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011644 int pc;
11645
11646 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011647 pc = pgetc();
11648 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011649 case '`':
11650 goto done;
11651
11652 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011653 pc = pgetc();
11654 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011655 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011656 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011657 /*
11658 * If eating a newline, avoid putting
11659 * the newline into the new character
11660 * stream (via the STPUTC after the
11661 * switch).
11662 */
11663 continue;
11664 }
11665 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011666 && (!dblquote || pc != '"')
11667 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011668 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011669 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011670 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011671 break;
11672 }
11673 /* fall through */
11674
11675 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011676 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011677 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011678 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011679
11680 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011681 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011682 needprompt = doprompt;
11683 break;
11684
11685 default:
11686 break;
11687 }
11688 STPUTC(pc, pout);
11689 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011690 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011691 STPUTC('\0', pout);
11692 psavelen = pout - (char *)stackblock();
11693 if (psavelen > 0) {
11694 pstr = grabstackstr(pout);
11695 setinputstring(pstr);
11696 }
11697 }
11698 nlpp = &bqlist;
11699 while (*nlpp)
11700 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011701 *nlpp = stzalloc(sizeof(**nlpp));
11702 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011703 parsebackquote = oldstyle;
11704
11705 if (oldstyle) {
11706 saveprompt = doprompt;
11707 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011708 }
11709
Eric Andersenc470f442003-07-28 09:56:35 +000011710 n = list(2);
11711
11712 if (oldstyle)
11713 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011714 else if (readtoken() != TRP)
11715 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011716
11717 (*nlpp)->n = n;
11718 if (oldstyle) {
11719 /*
11720 * Start reading from old file again, ignoring any pushed back
11721 * tokens left from the backquote parsing
11722 */
11723 popfile();
11724 tokpushback = 0;
11725 }
11726 while (stackblocksize() <= savelen)
11727 growstackblock();
11728 STARTSTACKSTR(out);
11729 if (str) {
11730 memcpy(out, str, savelen);
11731 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011732 INT_OFF;
11733 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011734 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011735 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011736 }
11737 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011738 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011739 if (arinest || dblquote)
11740 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11741 else
11742 USTPUTC(CTLBACKQ, out);
11743 if (oldstyle)
11744 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011745 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011746}
11747
Mike Frysinger98c52642009-04-02 10:02:37 +000011748#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011749/*
11750 * Parse an arithmetic expansion (indicate start of one and set state)
11751 */
Eric Andersenc470f442003-07-28 09:56:35 +000011752parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011753 if (++arinest == 1) {
11754 prevsyntax = syntax;
11755 syntax = ARISYNTAX;
11756 USTPUTC(CTLARI, out);
11757 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011758 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011759 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011760 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011761 } else {
11762 /*
11763 * we collapse embedded arithmetic expansion to
11764 * parenthesis, which should be equivalent
11765 */
11766 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011767 }
Eric Andersenc470f442003-07-28 09:56:35 +000011768 goto parsearith_return;
11769}
11770#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011771
Eric Andersenc470f442003-07-28 09:56:35 +000011772} /* end of readtoken */
11773
Eric Andersencb57d552001-06-28 07:25:16 +000011774/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011775 * Read the next input token.
11776 * If the token is a word, we set backquotelist to the list of cmds in
11777 * backquotes. We set quoteflag to true if any part of the word was
11778 * quoted.
11779 * If the token is TREDIR, then we set redirnode to a structure containing
11780 * the redirection.
11781 * In all cases, the variable startlinno is set to the number of the line
11782 * on which the token starts.
11783 *
11784 * [Change comment: here documents and internal procedures]
11785 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11786 * word parsing code into a separate routine. In this case, readtoken
11787 * doesn't need to have any internal procedures, but parseword does.
11788 * We could also make parseoperator in essence the main routine, and
11789 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011790 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011791#define NEW_xxreadtoken
11792#ifdef NEW_xxreadtoken
11793/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011794static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011795 '\n', '(', ')', /* singles */
11796 '&', '|', ';', /* doubles */
11797 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011798};
Eric Andersencb57d552001-06-28 07:25:16 +000011799
Denis Vlasenko834dee72008-10-07 09:18:30 +000011800#define xxreadtoken_singles 3
11801#define xxreadtoken_doubles 3
11802
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011803static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011804 TNL, TLP, TRP, /* only single occurrence allowed */
11805 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11806 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011807 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011808};
11809
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011810static int
11811xxreadtoken(void)
11812{
11813 int c;
11814
11815 if (tokpushback) {
11816 tokpushback = 0;
11817 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011818 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011819 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011820 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011821 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011822 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011823 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011824 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011825
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011826 if (c == '#') {
11827 while ((c = pgetc()) != '\n' && c != PEOF)
11828 continue;
11829 pungetc();
11830 } else if (c == '\\') {
11831 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011832 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011833 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011834 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011835 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011836 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011837 } else {
11838 const char *p;
11839
11840 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11841 if (c != PEOF) {
11842 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011843 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011844 needprompt = doprompt;
11845 }
11846
11847 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011848 if (p == NULL)
11849 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011850
Denis Vlasenko834dee72008-10-07 09:18:30 +000011851 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11852 int cc = pgetc();
11853 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011854 p += xxreadtoken_doubles + 1;
11855 } else {
11856 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011857#if ENABLE_ASH_BASH_COMPAT
11858 if (c == '&' && cc == '>') /* &> */
11859 break; /* return readtoken1(...) */
11860#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011861 }
11862 }
11863 }
11864 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11865 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011866 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011867 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011868
11869 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011870}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011871#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011872#define RETURN(token) return lasttoken = token
11873static int
11874xxreadtoken(void)
11875{
11876 int c;
11877
11878 if (tokpushback) {
11879 tokpushback = 0;
11880 return lasttoken;
11881 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011882 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011883 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011884 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011885 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011886 switch (c) {
11887 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011888 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011889 continue;
11890 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011891 while ((c = pgetc()) != '\n' && c != PEOF)
11892 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011893 pungetc();
11894 continue;
11895 case '\\':
11896 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011897 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011898 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011899 continue;
11900 }
11901 pungetc();
11902 goto breakloop;
11903 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011904 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011905 needprompt = doprompt;
11906 RETURN(TNL);
11907 case PEOF:
11908 RETURN(TEOF);
11909 case '&':
11910 if (pgetc() == '&')
11911 RETURN(TAND);
11912 pungetc();
11913 RETURN(TBACKGND);
11914 case '|':
11915 if (pgetc() == '|')
11916 RETURN(TOR);
11917 pungetc();
11918 RETURN(TPIPE);
11919 case ';':
11920 if (pgetc() == ';')
11921 RETURN(TENDCASE);
11922 pungetc();
11923 RETURN(TSEMI);
11924 case '(':
11925 RETURN(TLP);
11926 case ')':
11927 RETURN(TRP);
11928 default:
11929 goto breakloop;
11930 }
11931 }
11932 breakloop:
11933 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11934#undef RETURN
11935}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011936#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011937
11938static int
11939readtoken(void)
11940{
11941 int t;
11942#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011943 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011944#endif
11945
11946#if ENABLE_ASH_ALIAS
11947 top:
11948#endif
11949
11950 t = xxreadtoken();
11951
11952 /*
11953 * eat newlines
11954 */
11955 if (checkkwd & CHKNL) {
11956 while (t == TNL) {
11957 parseheredoc();
11958 t = xxreadtoken();
11959 }
11960 }
11961
11962 if (t != TWORD || quoteflag) {
11963 goto out;
11964 }
11965
11966 /*
11967 * check for keywords
11968 */
11969 if (checkkwd & CHKKWD) {
11970 const char *const *pp;
11971
11972 pp = findkwd(wordtext);
11973 if (pp) {
11974 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011975 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011976 goto out;
11977 }
11978 }
11979
11980 if (checkkwd & CHKALIAS) {
11981#if ENABLE_ASH_ALIAS
11982 struct alias *ap;
11983 ap = lookupalias(wordtext, 1);
11984 if (ap != NULL) {
11985 if (*ap->val) {
11986 pushstring(ap->val, ap);
11987 }
11988 goto top;
11989 }
11990#endif
11991 }
11992 out:
11993 checkkwd = 0;
11994#if DEBUG
11995 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011996 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011997 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011998 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011999#endif
12000 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000012001}
12002
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012003static char
12004peektoken(void)
12005{
12006 int t;
12007
12008 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012009 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012010 return tokname_array[t][0];
12011}
Eric Andersencb57d552001-06-28 07:25:16 +000012012
12013/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012014 * Read and parse a command. Returns NODE_EOF on end of file.
12015 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000012016 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012017static union node *
12018parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000012019{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012020 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012021
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012022 tokpushback = 0;
12023 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012024 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012025 needprompt = 0;
12026 t = readtoken();
12027 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012028 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012029 if (t == TNL)
12030 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012031 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012032 return list(1);
12033}
12034
12035/*
12036 * Input any here documents.
12037 */
12038static void
12039parseheredoc(void)
12040{
12041 struct heredoc *here;
12042 union node *n;
12043
12044 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012045 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012046
12047 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012048 setprompt_if(needprompt, 2);
12049 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012050 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012051 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012052 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012053 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012054 n->narg.text = wordtext;
12055 n->narg.backquote = backquotelist;
12056 here->here->nhere.doc = n;
12057 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012058 }
Eric Andersencb57d552001-06-28 07:25:16 +000012059}
12060
12061
12062/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012063 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012064 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012065#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012066static const char *
12067expandstr(const char *ps)
12068{
12069 union node n;
12070
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012071 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12072 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012073 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012074 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012075 popfile();
12076
12077 n.narg.type = NARG;
12078 n.narg.next = NULL;
12079 n.narg.text = wordtext;
12080 n.narg.backquote = backquotelist;
12081
12082 expandarg(&n, NULL, 0);
12083 return stackblock();
12084}
12085#endif
12086
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012087/*
12088 * Execute a command or commands contained in a string.
12089 */
12090static int
12091evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012092{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012093 union node *n;
12094 struct stackmark smark;
12095 int skip;
12096
12097 setinputstring(s);
12098 setstackmark(&smark);
12099
12100 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012101 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012102 evaltree(n, 0);
12103 popstackmark(&smark);
12104 skip = evalskip;
12105 if (skip)
12106 break;
12107 }
12108 popfile();
12109
12110 skip &= mask;
12111 evalskip = skip;
12112 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012113}
12114
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012115/*
12116 * The eval command.
12117 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012118static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012119evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012120{
12121 char *p;
12122 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012123
Denis Vlasenko68404f12008-03-17 09:00:54 +000012124 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012125 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012126 argv += 2;
12127 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012128 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012129 for (;;) {
12130 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012131 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012132 if (p == NULL)
12133 break;
12134 STPUTC(' ', concat);
12135 }
12136 STPUTC('\0', concat);
12137 p = grabstackstr(concat);
12138 }
12139 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012140 }
12141 return exitstatus;
12142}
12143
12144/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012145 * Read and execute commands.
12146 * "Top" is nonzero for the top level command loop;
12147 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012148 */
12149static int
12150cmdloop(int top)
12151{
12152 union node *n;
12153 struct stackmark smark;
12154 int inter;
12155 int numeof = 0;
12156
12157 TRACE(("cmdloop(%d) called\n", top));
12158 for (;;) {
12159 int skip;
12160
12161 setstackmark(&smark);
12162#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012163 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012164 showjobs(stderr, SHOW_CHANGED);
12165#endif
12166 inter = 0;
12167 if (iflag && top) {
12168 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012169 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012170 }
12171 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012172#if DEBUG
12173 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012174 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012175#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012176 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012177 if (!top || numeof >= 50)
12178 break;
12179 if (!stoppedjobs()) {
12180 if (!Iflag)
12181 break;
12182 out2str("\nUse \"exit\" to leave shell.\n");
12183 }
12184 numeof++;
12185 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012186 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12187 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012188 numeof = 0;
12189 evaltree(n, 0);
12190 }
12191 popstackmark(&smark);
12192 skip = evalskip;
12193
12194 if (skip) {
12195 evalskip = 0;
12196 return skip & SKIPEVAL;
12197 }
12198 }
12199 return 0;
12200}
12201
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012202/*
12203 * Take commands from a file. To be compatible we should do a path
12204 * search for the file, which is necessary to find sub-commands.
12205 */
12206static char *
12207find_dot_file(char *name)
12208{
12209 char *fullname;
12210 const char *path = pathval();
12211 struct stat statb;
12212
12213 /* don't try this for absolute or relative paths */
12214 if (strchr(name, '/'))
12215 return name;
12216
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012217 /* IIRC standards do not say whether . is to be searched.
12218 * And it is even smaller this way, making it unconditional for now:
12219 */
12220 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12221 fullname = name;
12222 goto try_cur_dir;
12223 }
12224
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012225 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012226 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012227 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12228 /*
12229 * Don't bother freeing here, since it will
12230 * be freed by the caller.
12231 */
12232 return fullname;
12233 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012234 if (fullname != name)
12235 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012236 }
12237
12238 /* not found in the PATH */
12239 ash_msg_and_raise_error("%s: not found", name);
12240 /* NOTREACHED */
12241}
12242
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012243static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012244dotcmd(int argc, char **argv)
12245{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012246 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012247 struct strlist *sp;
12248 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012249
12250 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012251 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012252
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012253 if (!argv[1]) {
12254 /* bash says: "bash: .: filename argument required" */
12255 return 2; /* bash compat */
12256 }
12257
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012258 /* "false; . empty_file; echo $?" should print 0, not 1: */
12259 exitstatus = 0;
12260
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012261 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012262
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012263 argv += 2;
12264 argc -= 2;
12265 if (argc) { /* argc > 0, argv[0] != NULL */
12266 saveparam = shellparam;
12267 shellparam.malloced = 0;
12268 shellparam.nparam = argc;
12269 shellparam.p = argv;
12270 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012271
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012272 setinputfile(fullname, INPUT_PUSH_FILE);
12273 commandname = fullname;
12274 cmdloop(0);
12275 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012276
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012277 if (argc) {
12278 freeparam(&shellparam);
12279 shellparam = saveparam;
12280 };
12281
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012282 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012283}
12284
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012285static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012286exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012287{
12288 if (stoppedjobs())
12289 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012290 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012291 exitstatus = number(argv[1]);
12292 raise_exception(EXEXIT);
12293 /* NOTREACHED */
12294}
12295
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012296/*
12297 * Read a file containing shell functions.
12298 */
12299static void
12300readcmdfile(char *name)
12301{
12302 setinputfile(name, INPUT_PUSH_FILE);
12303 cmdloop(0);
12304 popfile();
12305}
12306
12307
Denis Vlasenkocc571512007-02-23 21:10:35 +000012308/* ============ find_command inplementation */
12309
12310/*
12311 * Resolve a command name. If you change this routine, you may have to
12312 * change the shellexec routine as well.
12313 */
12314static void
12315find_command(char *name, struct cmdentry *entry, int act, const char *path)
12316{
12317 struct tblentry *cmdp;
12318 int idx;
12319 int prev;
12320 char *fullname;
12321 struct stat statb;
12322 int e;
12323 int updatetbl;
12324 struct builtincmd *bcmd;
12325
12326 /* If name contains a slash, don't use PATH or hash table */
12327 if (strchr(name, '/') != NULL) {
12328 entry->u.index = -1;
12329 if (act & DO_ABS) {
12330 while (stat(name, &statb) < 0) {
12331#ifdef SYSV
12332 if (errno == EINTR)
12333 continue;
12334#endif
12335 entry->cmdtype = CMDUNKNOWN;
12336 return;
12337 }
12338 }
12339 entry->cmdtype = CMDNORMAL;
12340 return;
12341 }
12342
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012343/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012344
12345 updatetbl = (path == pathval());
12346 if (!updatetbl) {
12347 act |= DO_ALTPATH;
12348 if (strstr(path, "%builtin") != NULL)
12349 act |= DO_ALTBLTIN;
12350 }
12351
12352 /* If name is in the table, check answer will be ok */
12353 cmdp = cmdlookup(name, 0);
12354 if (cmdp != NULL) {
12355 int bit;
12356
12357 switch (cmdp->cmdtype) {
12358 default:
12359#if DEBUG
12360 abort();
12361#endif
12362 case CMDNORMAL:
12363 bit = DO_ALTPATH;
12364 break;
12365 case CMDFUNCTION:
12366 bit = DO_NOFUNC;
12367 break;
12368 case CMDBUILTIN:
12369 bit = DO_ALTBLTIN;
12370 break;
12371 }
12372 if (act & bit) {
12373 updatetbl = 0;
12374 cmdp = NULL;
12375 } else if (cmdp->rehash == 0)
12376 /* if not invalidated by cd, we're done */
12377 goto success;
12378 }
12379
12380 /* If %builtin not in path, check for builtin next */
12381 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012382 if (bcmd) {
12383 if (IS_BUILTIN_REGULAR(bcmd))
12384 goto builtin_success;
12385 if (act & DO_ALTPATH) {
12386 if (!(act & DO_ALTBLTIN))
12387 goto builtin_success;
12388 } else if (builtinloc <= 0) {
12389 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012390 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012391 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012392
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012393#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012394 {
12395 int applet_no = find_applet_by_name(name);
12396 if (applet_no >= 0) {
12397 entry->cmdtype = CMDNORMAL;
12398 entry->u.index = -2 - applet_no;
12399 return;
12400 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012401 }
12402#endif
12403
Denis Vlasenkocc571512007-02-23 21:10:35 +000012404 /* We have to search path. */
12405 prev = -1; /* where to start */
12406 if (cmdp && cmdp->rehash) { /* doing a rehash */
12407 if (cmdp->cmdtype == CMDBUILTIN)
12408 prev = builtinloc;
12409 else
12410 prev = cmdp->param.index;
12411 }
12412
12413 e = ENOENT;
12414 idx = -1;
12415 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012416 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012417 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012418 /* NB: code below will still use fullname
12419 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012420 idx++;
12421 if (pathopt) {
12422 if (prefix(pathopt, "builtin")) {
12423 if (bcmd)
12424 goto builtin_success;
12425 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012426 }
12427 if ((act & DO_NOFUNC)
12428 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012429 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012430 continue;
12431 }
12432 }
12433 /* if rehash, don't redo absolute path names */
12434 if (fullname[0] == '/' && idx <= prev) {
12435 if (idx < prev)
12436 continue;
12437 TRACE(("searchexec \"%s\": no change\n", name));
12438 goto success;
12439 }
12440 while (stat(fullname, &statb) < 0) {
12441#ifdef SYSV
12442 if (errno == EINTR)
12443 continue;
12444#endif
12445 if (errno != ENOENT && errno != ENOTDIR)
12446 e = errno;
12447 goto loop;
12448 }
12449 e = EACCES; /* if we fail, this will be the error */
12450 if (!S_ISREG(statb.st_mode))
12451 continue;
12452 if (pathopt) { /* this is a %func directory */
12453 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012454 /* NB: stalloc will return space pointed by fullname
12455 * (because we don't have any intervening allocations
12456 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012457 readcmdfile(fullname);
12458 cmdp = cmdlookup(name, 0);
12459 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12460 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12461 stunalloc(fullname);
12462 goto success;
12463 }
12464 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12465 if (!updatetbl) {
12466 entry->cmdtype = CMDNORMAL;
12467 entry->u.index = idx;
12468 return;
12469 }
12470 INT_OFF;
12471 cmdp = cmdlookup(name, 1);
12472 cmdp->cmdtype = CMDNORMAL;
12473 cmdp->param.index = idx;
12474 INT_ON;
12475 goto success;
12476 }
12477
12478 /* We failed. If there was an entry for this command, delete it */
12479 if (cmdp && updatetbl)
12480 delete_cmd_entry();
12481 if (act & DO_ERR)
12482 ash_msg("%s: %s", name, errmsg(e, "not found"));
12483 entry->cmdtype = CMDUNKNOWN;
12484 return;
12485
12486 builtin_success:
12487 if (!updatetbl) {
12488 entry->cmdtype = CMDBUILTIN;
12489 entry->u.cmd = bcmd;
12490 return;
12491 }
12492 INT_OFF;
12493 cmdp = cmdlookup(name, 1);
12494 cmdp->cmdtype = CMDBUILTIN;
12495 cmdp->param.cmd = bcmd;
12496 INT_ON;
12497 success:
12498 cmdp->rehash = 0;
12499 entry->cmdtype = cmdp->cmdtype;
12500 entry->u = cmdp->param;
12501}
12502
12503
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012504/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012505
Eric Andersencb57d552001-06-28 07:25:16 +000012506/*
Eric Andersencb57d552001-06-28 07:25:16 +000012507 * The trap builtin.
12508 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012509static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012510trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012511{
12512 char *action;
12513 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012514 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012515
Eric Andersenc470f442003-07-28 09:56:35 +000012516 nextopt(nullstr);
12517 ap = argptr;
12518 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012519 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012520 char *tr = trap_ptr[signo];
12521 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012522 /* note: bash adds "SIG", but only if invoked
12523 * as "bash". If called as "sh", or if set -o posix,
12524 * then it prints short signal names.
12525 * We are printing short names: */
12526 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012527 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012528 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012529 /* trap_ptr != trap only if we are in special-cased `trap` code.
12530 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012531 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012532 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012533 }
12534 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012535 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012536 if (trap_ptr != trap) {
12537 free(trap_ptr);
12538 trap_ptr = trap;
12539 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012540 */
Eric Andersencb57d552001-06-28 07:25:16 +000012541 return 0;
12542 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012543
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012544 action = NULL;
12545 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012546 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012547 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012548 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012549 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012550 if (signo < 0) {
12551 /* Mimic bash message exactly */
12552 ash_msg("%s: invalid signal specification", *ap);
12553 exitcode = 1;
12554 goto next;
12555 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012556 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012557 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012558 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012559 action = NULL;
12560 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012561 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012562 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012563 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012564 if (action)
12565 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012566 trap[signo] = action;
12567 if (signo != 0)
12568 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012569 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012570 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012571 ap++;
12572 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012573 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012574}
12575
Eric Andersenc470f442003-07-28 09:56:35 +000012576
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012577/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012578
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012579#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012580/*
12581 * Lists available builtins
12582 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012583static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012584helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012585{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012586 unsigned col;
12587 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012588
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012589 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012590 "Built-in commands:\n"
12591 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012592 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012593 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012594 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012595 if (col > 60) {
12596 out1fmt("\n");
12597 col = 0;
12598 }
12599 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012600#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012601 {
12602 const char *a = applet_names;
12603 while (*a) {
12604 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12605 if (col > 60) {
12606 out1fmt("\n");
12607 col = 0;
12608 }
12609 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012610 }
12611 }
12612#endif
12613 out1fmt("\n\n");
12614 return EXIT_SUCCESS;
12615}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012616#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012617
Eric Andersencb57d552001-06-28 07:25:16 +000012618/*
Eric Andersencb57d552001-06-28 07:25:16 +000012619 * The export and readonly commands.
12620 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012621static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012622exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012623{
12624 struct var *vp;
12625 char *name;
12626 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012627 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020012628 char opt;
12629 int flag;
12630 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000012631
Denys Vlasenkod5275882012-10-01 13:41:17 +020012632 /* "readonly" in bash accepts, but ignores -n.
12633 * We do the same: it saves a conditional in nextopt's param.
12634 */
12635 flag_off = 0;
12636 while ((opt = nextopt("np")) != '\0') {
12637 if (opt == 'n')
12638 flag_off = VEXPORT;
12639 }
12640 flag = VEXPORT;
12641 if (argv[0][0] == 'r') {
12642 flag = VREADONLY;
12643 flag_off = 0; /* readonly ignores -n */
12644 }
12645 flag_off = ~flag_off;
12646
12647 /*if (opt_p_not_specified) - bash doesnt check this. Try "export -p NAME" */
12648 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012649 aptr = argptr;
12650 name = *aptr;
12651 if (name) {
12652 do {
12653 p = strchr(name, '=');
12654 if (p != NULL) {
12655 p++;
12656 } else {
12657 vp = *findvar(hashvar(name), name);
12658 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020012659 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012660 continue;
12661 }
Eric Andersencb57d552001-06-28 07:25:16 +000012662 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020012663 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012664 } while ((name = *++aptr) != NULL);
12665 return 0;
12666 }
Eric Andersencb57d552001-06-28 07:25:16 +000012667 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020012668
12669 /* No arguments. Show the list of exported or readonly vars.
12670 * -n is ignored.
12671 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012672 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012673 return 0;
12674}
12675
Eric Andersencb57d552001-06-28 07:25:16 +000012676/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012677 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012678 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012679static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012680unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012681{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012682 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012683
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012684 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012685 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012686 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012687}
12688
Eric Andersencb57d552001-06-28 07:25:16 +000012689/*
Eric Andersencb57d552001-06-28 07:25:16 +000012690 * The unset builtin command. We unset the function before we unset the
12691 * variable to allow a function to be unset when there is a readonly variable
12692 * with the same name.
12693 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012694static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012695unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012696{
12697 char **ap;
12698 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012699 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012700 int ret = 0;
12701
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012702 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012703 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012704 }
Eric Andersencb57d552001-06-28 07:25:16 +000012705
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012706 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012707 if (flag != 'f') {
12708 i = unsetvar(*ap);
12709 ret |= i;
12710 if (!(i & 2))
12711 continue;
12712 }
12713 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012714 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012715 }
Eric Andersenc470f442003-07-28 09:56:35 +000012716 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012717}
12718
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012719static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012720 ' ', offsetof(struct tms, tms_utime),
12721 '\n', offsetof(struct tms, tms_stime),
12722 ' ', offsetof(struct tms, tms_cutime),
12723 '\n', offsetof(struct tms, tms_cstime),
12724 0
12725};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012726static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012727timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012728{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012729 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012730 const unsigned char *p;
12731 struct tms buf;
12732
12733 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012734 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012735
12736 p = timescmd_str;
12737 do {
12738 t = *(clock_t *)(((char *) &buf) + p[1]);
12739 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012740 t = t % clk_tck;
12741 out1fmt("%lum%lu.%03lus%c",
12742 s / 60, s % 60,
12743 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012744 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012745 p += 2;
12746 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012747
Eric Andersencb57d552001-06-28 07:25:16 +000012748 return 0;
12749}
12750
Mike Frysinger98c52642009-04-02 10:02:37 +000012751#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012752/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012753 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012754 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012755 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012756 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012757 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012758static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012759letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012760{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012761 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012762
Denis Vlasenko68404f12008-03-17 09:00:54 +000012763 argv++;
12764 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012765 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012766 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012767 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012768 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012769
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012770 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012771}
Eric Andersenc470f442003-07-28 09:56:35 +000012772#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012773
Eric Andersenc470f442003-07-28 09:56:35 +000012774/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012775 * The read builtin. Options:
12776 * -r Do not interpret '\' specially
12777 * -s Turn off echo (tty only)
12778 * -n NCHARS Read NCHARS max
12779 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12780 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12781 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012782 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012783 * TODO: bash also has:
12784 * -a ARRAY Read into array[0],[1],etc
12785 * -d DELIM End on DELIM char, not newline
12786 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012787 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012788static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012789readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012790{
Denys Vlasenko73067272010-01-12 22:11:24 +010012791 char *opt_n = NULL;
12792 char *opt_p = NULL;
12793 char *opt_t = NULL;
12794 char *opt_u = NULL;
12795 int read_flags = 0;
12796 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012797 int i;
12798
Denys Vlasenko73067272010-01-12 22:11:24 +010012799 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012800 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012801 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012802 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012803 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012804 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012805 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012806 break;
12807 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012808 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012809 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012810 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012811 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012812 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012813 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012814 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012815 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012816 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012817 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012818 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012819 default:
12820 break;
12821 }
Eric Andersenc470f442003-07-28 09:56:35 +000012822 }
Paul Fox02eb9342005-09-07 16:56:02 +000012823
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020012824 /* "read -s" needs to save/restore termios, can't allow ^C
12825 * to jump out of it.
12826 */
12827 INT_OFF;
Denys Vlasenko03dad222010-01-12 23:29:57 +010012828 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012829 argptr,
12830 bltinlookup("IFS"), /* can be NULL */
12831 read_flags,
12832 opt_n,
12833 opt_p,
12834 opt_t,
12835 opt_u
12836 );
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020012837 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012838
Denys Vlasenko73067272010-01-12 22:11:24 +010012839 if ((uintptr_t)r > 1)
12840 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012841
Denys Vlasenko73067272010-01-12 22:11:24 +010012842 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012843}
12844
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012845static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012846umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012847{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012848 static const char permuser[3] ALIGN1 = "ugo";
12849 static const char permmode[3] ALIGN1 = "rwx";
12850 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012851 S_IRUSR, S_IWUSR, S_IXUSR,
12852 S_IRGRP, S_IWGRP, S_IXGRP,
12853 S_IROTH, S_IWOTH, S_IXOTH
12854 };
12855
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012856 /* TODO: use bb_parse_mode() instead */
12857
Eric Andersenc470f442003-07-28 09:56:35 +000012858 char *ap;
12859 mode_t mask;
12860 int i;
12861 int symbolic_mode = 0;
12862
12863 while (nextopt("S") != '\0') {
12864 symbolic_mode = 1;
12865 }
12866
Denis Vlasenkob012b102007-02-19 22:43:01 +000012867 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012868 mask = umask(0);
12869 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012870 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012871
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012872 ap = *argptr;
12873 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012874 if (symbolic_mode) {
12875 char buf[18];
12876 char *p = buf;
12877
12878 for (i = 0; i < 3; i++) {
12879 int j;
12880
12881 *p++ = permuser[i];
12882 *p++ = '=';
12883 for (j = 0; j < 3; j++) {
12884 if ((mask & permmask[3 * i + j]) == 0) {
12885 *p++ = permmode[j];
12886 }
12887 }
12888 *p++ = ',';
12889 }
12890 *--p = 0;
12891 puts(buf);
12892 } else {
12893 out1fmt("%.4o\n", mask);
12894 }
12895 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012896 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012897 mask = 0;
12898 do {
12899 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012900 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012901 mask = (mask << 3) + (*ap - '0');
12902 } while (*++ap != '\0');
12903 umask(mask);
12904 } else {
12905 mask = ~mask & 0777;
12906 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012907 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012908 }
12909 umask(~mask & 0777);
12910 }
12911 }
12912 return 0;
12913}
12914
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012915static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012916ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012917{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012918 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012919}
12920
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012921/* ============ main() and helpers */
12922
12923/*
12924 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012925 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012926static void
12927exitshell(void)
12928{
12929 struct jmploc loc;
12930 char *p;
12931 int status;
12932
Denys Vlasenkobede2152011-09-04 16:12:33 +020012933#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
12934 save_history(line_input_state);
12935#endif
12936
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012937 status = exitstatus;
12938 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12939 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012940 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012941/* dash bug: it just does _exit(exitstatus) here
12942 * but we have to do setjobctl(0) first!
12943 * (bug is still not fixed in dash-0.5.3 - if you run dash
12944 * under Midnight Commander, on exit from dash MC is backgrounded) */
12945 status = exitstatus;
12946 goto out;
12947 }
12948 exception_handler = &loc;
12949 p = trap[0];
12950 if (p) {
12951 trap[0] = NULL;
12952 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012953 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012954 }
12955 flush_stdout_stderr();
12956 out:
12957 setjobctl(0);
12958 _exit(status);
12959 /* NOTREACHED */
12960}
12961
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012962static void
12963init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012964{
12965 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012966 /* we will never free this */
12967 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012968
12969 /* from trap.c: */
12970 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012971 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12972 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12973 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012974 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012975
12976 /* from var.c: */
12977 {
12978 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012979 const char *p;
12980 struct stat st1, st2;
12981
12982 initvar();
12983 for (envp = environ; envp && *envp; envp++) {
12984 if (strchr(*envp, '=')) {
12985 setvareq(*envp, VEXPORT|VTEXTFIXED);
12986 }
12987 }
12988
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012989 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012990
12991 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012992 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012993 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012994 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12995 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012996 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012997 }
12998 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012999 setpwd(p, 0);
13000 }
13001}
13002
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013003
13004//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010013005//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013006//usage:#define ash_full_usage "\n\n"
13007//usage: "Unix shell interpreter"
13008
13009//usage:#if ENABLE_FEATURE_SH_IS_ASH
13010//usage:# define sh_trivial_usage ash_trivial_usage
13011//usage:# define sh_full_usage ash_full_usage
13012//usage:#endif
13013//usage:#if ENABLE_FEATURE_BASH_IS_ASH
13014//usage:# define bash_trivial_usage ash_trivial_usage
13015//usage:# define bash_full_usage ash_full_usage
13016//usage:#endif
13017
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013018/*
13019 * Process the shell command line arguments.
13020 */
13021static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013022procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013023{
13024 int i;
13025 const char *xminusc;
13026 char **xargv;
13027
13028 xargv = argv;
13029 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013030 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013031 xargv++;
13032 for (i = 0; i < NOPTS; i++)
13033 optlist[i] = 2;
13034 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013035 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013036 /* it already printed err message */
13037 raise_exception(EXERROR);
13038 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013039 xargv = argptr;
13040 xminusc = minusc;
13041 if (*xargv == NULL) {
13042 if (xminusc)
13043 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13044 sflag = 1;
13045 }
13046 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13047 iflag = 1;
13048 if (mflag == 2)
13049 mflag = iflag;
13050 for (i = 0; i < NOPTS; i++)
13051 if (optlist[i] == 2)
13052 optlist[i] = 0;
13053#if DEBUG == 2
13054 debug = 1;
13055#endif
13056 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13057 if (xminusc) {
13058 minusc = *xargv++;
13059 if (*xargv)
13060 goto setarg0;
13061 } else if (!sflag) {
13062 setinputfile(*xargv, 0);
13063 setarg0:
13064 arg0 = *xargv++;
13065 commandname = arg0;
13066 }
13067
13068 shellparam.p = xargv;
13069#if ENABLE_ASH_GETOPTS
13070 shellparam.optind = 1;
13071 shellparam.optoff = -1;
13072#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013073 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013074 while (*xargv) {
13075 shellparam.nparam++;
13076 xargv++;
13077 }
13078 optschanged();
13079}
13080
13081/*
13082 * Read /etc/profile or .profile.
13083 */
13084static void
13085read_profile(const char *name)
13086{
13087 int skip;
13088
13089 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13090 return;
13091 skip = cmdloop(0);
13092 popfile();
13093 if (skip)
13094 exitshell();
13095}
13096
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013097/*
13098 * This routine is called when an error or an interrupt occurs in an
13099 * interactive shell and control is returned to the main command loop.
13100 */
13101static void
13102reset(void)
13103{
13104 /* from eval.c: */
13105 evalskip = 0;
13106 loopnest = 0;
13107 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013108 g_parsefile->left_in_buffer = 0;
13109 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013110 popallfiles();
13111 /* from parser.c: */
13112 tokpushback = 0;
13113 checkkwd = 0;
13114 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013115 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013116}
13117
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013118#if PROFILE
13119static short profile_buf[16384];
13120extern int etext();
13121#endif
13122
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013123/*
13124 * Main routine. We initialize things, parse the arguments, execute
13125 * profiles if we're a login shell, and then call cmdloop to execute
13126 * commands. The setjmp call sets up the location to jump to when an
13127 * exception occurs. When an exception occurs the variable "state"
13128 * is used to figure out how far we had gotten.
13129 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013130int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013131int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013132{
Mike Frysinger98c52642009-04-02 10:02:37 +000013133 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013134 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013135 struct jmploc jmploc;
13136 struct stackmark smark;
13137
Denis Vlasenko01631112007-12-16 17:20:38 +000013138 /* Initialize global data */
13139 INIT_G_misc();
13140 INIT_G_memstack();
13141 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013142#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013143 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013144#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013145 INIT_G_cmdtable();
13146
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013147#if PROFILE
13148 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13149#endif
13150
13151#if ENABLE_FEATURE_EDITING
13152 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13153#endif
13154 state = 0;
13155 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013156 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013157 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013158
13159 reset();
13160
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013161 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013162 if (e == EXERROR)
13163 exitstatus = 2;
13164 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013165 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013166 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013167 }
13168 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013169 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013170 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013171
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013172 popstackmark(&smark);
13173 FORCE_INT_ON; /* enable interrupts */
13174 if (s == 1)
13175 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013176 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013177 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013178 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013179 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013180 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013181 }
13182 exception_handler = &jmploc;
13183#if DEBUG
13184 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013185 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013186 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013187#endif
13188 rootpid = getpid();
13189
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013190 init();
13191 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013192 procargs(argv);
13193
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013194#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13195 if (iflag) {
13196 const char *hp = lookupvar("HISTFILE");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013197 if (!hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013198 hp = lookupvar("HOME");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013199 if (hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013200 char *defhp = concat_path_file(hp, ".ash_history");
13201 setvar("HISTFILE", defhp, 0);
13202 free(defhp);
13203 }
13204 }
13205 }
13206#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013207 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013208 isloginsh = 1;
13209 if (isloginsh) {
13210 state = 1;
13211 read_profile("/etc/profile");
13212 state1:
13213 state = 2;
13214 read_profile(".profile");
13215 }
13216 state2:
13217 state = 3;
13218 if (
13219#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013220 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013221#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013222 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013223 ) {
13224 shinit = lookupvar("ENV");
13225 if (shinit != NULL && *shinit != '\0') {
13226 read_profile(shinit);
13227 }
13228 }
13229 state3:
13230 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013231 if (minusc) {
13232 /* evalstring pushes parsefile stack.
13233 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013234 * is one of stacked source fds.
13235 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013236 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013237 // ^^ not necessary since now we special-case fd 0
13238 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013239 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013240 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013241
13242 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020013243#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013244 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013245 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013246 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013247 line_input_state->hist_file = hp;
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013248# if ENABLE_FEATURE_SH_HISTFILESIZE
13249 hp = lookupvar("HISTFILESIZE");
13250 line_input_state->max_history = size_from_HISTFILESIZE(hp);
13251# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013252 }
13253#endif
13254 state4: /* XXX ??? - why isn't this before the "if" statement */
13255 cmdloop(1);
13256 }
13257#if PROFILE
13258 monitor(0);
13259#endif
13260#ifdef GPROF
13261 {
13262 extern void _mcleanup(void);
13263 _mcleanup();
13264 }
13265#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013266 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013267 exitshell();
13268 /* NOTREACHED */
13269}
13270
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013271
Eric Andersendf82f612001-06-28 07:46:40 +000013272/*-
13273 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013274 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013275 *
13276 * This code is derived from software contributed to Berkeley by
13277 * Kenneth Almquist.
13278 *
13279 * Redistribution and use in source and binary forms, with or without
13280 * modification, are permitted provided that the following conditions
13281 * are met:
13282 * 1. Redistributions of source code must retain the above copyright
13283 * notice, this list of conditions and the following disclaimer.
13284 * 2. Redistributions in binary form must reproduce the above copyright
13285 * notice, this list of conditions and the following disclaimer in the
13286 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013287 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013288 * may be used to endorse or promote products derived from this software
13289 * without specific prior written permission.
13290 *
13291 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13292 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13293 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13294 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13295 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13296 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13297 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13298 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13299 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13300 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13301 * SUCH DAMAGE.
13302 */