blob: dda18e8b55283207a3523a2140c42ab5827072c5 [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
Denis Vlasenko559691a2008-10-05 18:39:31 +0000388static int isdigit_str9(const char *str)
389{
390 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
391 while (--maxlen && isdigit(*str))
392 str++;
393 return (*str == '\0');
394}
Denis Vlasenko01631112007-12-16 17:20:38 +0000395
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200396static const char *var_end(const char *var)
397{
398 while (*var)
399 if (*var++ == '=')
400 break;
401 return var;
402}
403
Denis Vlasenko559691a2008-10-05 18:39:31 +0000404
405/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100406
407static void exitshell(void) NORETURN;
408
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000409/*
Eric Andersen2870d962001-07-02 17:27:21 +0000410 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000411 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000412 * much more efficient and portable. (But hacking the kernel is so much
413 * more fun than worrying about efficiency and portability. :-))
414 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000415#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200416 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000417 xbarrier(); \
418} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000419
420/*
421 * Called to raise an exception. Since C doesn't include exceptions, we
422 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000423 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000424 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000425static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000426static void
427raise_exception(int e)
428{
429#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000430 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000431 abort();
432#endif
433 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000434 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000435 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000436}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000437#if DEBUG
438#define raise_exception(e) do { \
439 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
440 raise_exception(e); \
441} while (0)
442#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000443
444/*
445 * Called from trap.c when a SIGINT is received. (If the user specifies
446 * that SIGINT is to be trapped or ignored using the trap builtin, then
447 * this routine is not called.) Suppressint is nonzero when interrupts
448 * are held using the INT_OFF macro. (The test for iflag is just
449 * defensive programming.)
450 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000451static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000452static void
453raise_interrupt(void)
454{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000455 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000456
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200457 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000458 /* Signal is not automatically unmasked after it is raised,
459 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000460 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200461 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000462
Denis Vlasenko4b875702009-03-19 13:30:04 +0000463 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000464 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
465 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000466 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000467 signal(SIGINT, SIG_DFL);
468 raise(SIGINT);
469 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000470 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000471 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000472 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000473 /* NOTREACHED */
474}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000475#if DEBUG
476#define raise_interrupt() do { \
477 TRACE(("raising interrupt on line %d\n", __LINE__)); \
478 raise_interrupt(); \
479} while (0)
480#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000481
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000482static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000483int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000484{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000485 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200486 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000487 raise_interrupt();
488 }
489}
490#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000491static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000492force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000493{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000494 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200495 suppress_int = 0;
496 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000497 raise_interrupt();
498}
499#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000500
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200501#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000502
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000503#define RESTORE_INT(v) do { \
504 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200505 suppress_int = (v); \
506 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000507 raise_interrupt(); \
508} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000509
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000510
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000511/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000512
Eric Andersenc470f442003-07-28 09:56:35 +0000513static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000514outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000515{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000516 INT_OFF;
517 fputs(p, file);
518 INT_ON;
519}
520
521static void
522flush_stdout_stderr(void)
523{
524 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100525 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000526 INT_ON;
527}
528
529static void
530outcslow(int c, FILE *dest)
531{
532 INT_OFF;
533 putc(c, dest);
534 fflush(dest);
535 INT_ON;
536}
537
538static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
539static int
540out1fmt(const char *fmt, ...)
541{
542 va_list ap;
543 int r;
544
545 INT_OFF;
546 va_start(ap, fmt);
547 r = vprintf(fmt, ap);
548 va_end(ap);
549 INT_ON;
550 return r;
551}
552
553static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
554static int
555fmtstr(char *outbuf, size_t length, const char *fmt, ...)
556{
557 va_list ap;
558 int ret;
559
560 va_start(ap, fmt);
561 INT_OFF;
562 ret = vsnprintf(outbuf, length, fmt, ap);
563 va_end(ap);
564 INT_ON;
565 return ret;
566}
567
568static void
569out1str(const char *p)
570{
571 outstr(p, stdout);
572}
573
574static void
575out2str(const char *p)
576{
577 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100578 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000579}
580
581
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000582/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000583
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000584/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100585#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200586#define CTLESC ((unsigned char)'\201') /* escape next character */
587#define CTLVAR ((unsigned char)'\202') /* variable defn */
588#define CTLENDVAR ((unsigned char)'\203')
589#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000590#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
591/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200592#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
593#define CTLENDARI ((unsigned char)'\207')
594#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100595#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000596
597/* variable substitution byte (follows CTLVAR) */
598#define VSTYPE 0x0f /* type of variable substitution */
599#define VSNUL 0x10 /* colon--treat the empty string as unset */
600#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
601
602/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000603#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
604#define VSMINUS 0x2 /* ${var-text} */
605#define VSPLUS 0x3 /* ${var+text} */
606#define VSQUESTION 0x4 /* ${var?message} */
607#define VSASSIGN 0x5 /* ${var=text} */
608#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
609#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
610#define VSTRIMLEFT 0x8 /* ${var#pattern} */
611#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
612#define VSLENGTH 0xa /* ${#var} */
613#if ENABLE_ASH_BASH_COMPAT
614#define VSSUBSTR 0xc /* ${var:position:length} */
615#define VSREPLACE 0xd /* ${var/pattern/replacement} */
616#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
617#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000618
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000619static const char dolatstr[] ALIGN1 = {
620 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
621};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000622
Denis Vlasenko559691a2008-10-05 18:39:31 +0000623#define NCMD 0
624#define NPIPE 1
625#define NREDIR 2
626#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000627#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000628#define NAND 5
629#define NOR 6
630#define NSEMI 7
631#define NIF 8
632#define NWHILE 9
633#define NUNTIL 10
634#define NFOR 11
635#define NCASE 12
636#define NCLIST 13
637#define NDEFUN 14
638#define NARG 15
639#define NTO 16
640#if ENABLE_ASH_BASH_COMPAT
641#define NTO2 17
642#endif
643#define NCLOBBER 18
644#define NFROM 19
645#define NFROMTO 20
646#define NAPPEND 21
647#define NTOFD 22
648#define NFROMFD 23
649#define NHERE 24
650#define NXHERE 25
651#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000652#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000653
654union node;
655
656struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000657 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000658 union node *assign;
659 union node *args;
660 union node *redirect;
661};
662
663struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000664 smallint type;
665 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000666 struct nodelist *cmdlist;
667};
668
669struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000670 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000671 union node *n;
672 union node *redirect;
673};
674
675struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000676 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000677 union node *ch1;
678 union node *ch2;
679};
680
681struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000682 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000683 union node *test;
684 union node *ifpart;
685 union node *elsepart;
686};
687
688struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000689 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000690 union node *args;
691 union node *body;
692 char *var;
693};
694
695struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000696 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000697 union node *expr;
698 union node *cases;
699};
700
701struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000702 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000703 union node *next;
704 union node *pattern;
705 union node *body;
706};
707
708struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000709 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000710 union node *next;
711 char *text;
712 struct nodelist *backquote;
713};
714
Denis Vlasenko559691a2008-10-05 18:39:31 +0000715/* nfile and ndup layout must match!
716 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
717 * that it is actually NTO2 (>&file), and change its type.
718 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000719struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000720 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000721 union node *next;
722 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000723 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000724 union node *fname;
725 char *expfname;
726};
727
728struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000729 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000730 union node *next;
731 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000732 int dupfd;
733 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000734 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000735};
736
737struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000738 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000739 union node *next;
740 int fd;
741 union node *doc;
742};
743
744struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000745 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000746 union node *com;
747};
748
749union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000750 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000751 struct ncmd ncmd;
752 struct npipe npipe;
753 struct nredir nredir;
754 struct nbinary nbinary;
755 struct nif nif;
756 struct nfor nfor;
757 struct ncase ncase;
758 struct nclist nclist;
759 struct narg narg;
760 struct nfile nfile;
761 struct ndup ndup;
762 struct nhere nhere;
763 struct nnot nnot;
764};
765
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200766/*
767 * NODE_EOF is returned by parsecmd when it encounters an end of file.
768 * It must be distinct from NULL.
769 */
770#define NODE_EOF ((union node *) -1L)
771
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000772struct nodelist {
773 struct nodelist *next;
774 union node *n;
775};
776
777struct funcnode {
778 int count;
779 union node n;
780};
781
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000782/*
783 * Free a parse tree.
784 */
785static void
786freefunc(struct funcnode *f)
787{
788 if (f && --f->count < 0)
789 free(f);
790}
791
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000792
793/* ============ Debugging output */
794
795#if DEBUG
796
797static FILE *tracefile;
798
799static void
800trace_printf(const char *fmt, ...)
801{
802 va_list va;
803
804 if (debug != 1)
805 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000806 if (DEBUG_TIME)
807 fprintf(tracefile, "%u ", (int) time(NULL));
808 if (DEBUG_PID)
809 fprintf(tracefile, "[%u] ", (int) getpid());
810 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200811 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000812 va_start(va, fmt);
813 vfprintf(tracefile, fmt, va);
814 va_end(va);
815}
816
817static void
818trace_vprintf(const char *fmt, va_list va)
819{
820 if (debug != 1)
821 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000822 if (DEBUG_TIME)
823 fprintf(tracefile, "%u ", (int) time(NULL));
824 if (DEBUG_PID)
825 fprintf(tracefile, "[%u] ", (int) getpid());
826 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200827 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000828 vfprintf(tracefile, fmt, va);
829}
830
831static void
832trace_puts(const char *s)
833{
834 if (debug != 1)
835 return;
836 fputs(s, tracefile);
837}
838
839static void
840trace_puts_quoted(char *s)
841{
842 char *p;
843 char c;
844
845 if (debug != 1)
846 return;
847 putc('"', tracefile);
848 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100849 switch ((unsigned char)*p) {
850 case '\n': c = 'n'; goto backslash;
851 case '\t': c = 't'; goto backslash;
852 case '\r': c = 'r'; goto backslash;
853 case '\"': c = '\"'; goto backslash;
854 case '\\': c = '\\'; goto backslash;
855 case CTLESC: c = 'e'; goto backslash;
856 case CTLVAR: c = 'v'; goto backslash;
857 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
858 case CTLBACKQ: c = 'q'; goto backslash;
859 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000860 backslash:
861 putc('\\', tracefile);
862 putc(c, tracefile);
863 break;
864 default:
865 if (*p >= ' ' && *p <= '~')
866 putc(*p, tracefile);
867 else {
868 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100869 putc((*p >> 6) & 03, tracefile);
870 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000871 putc(*p & 07, tracefile);
872 }
873 break;
874 }
875 }
876 putc('"', tracefile);
877}
878
879static void
880trace_puts_args(char **ap)
881{
882 if (debug != 1)
883 return;
884 if (!*ap)
885 return;
886 while (1) {
887 trace_puts_quoted(*ap);
888 if (!*++ap) {
889 putc('\n', tracefile);
890 break;
891 }
892 putc(' ', tracefile);
893 }
894}
895
896static void
897opentrace(void)
898{
899 char s[100];
900#ifdef O_APPEND
901 int flags;
902#endif
903
904 if (debug != 1) {
905 if (tracefile)
906 fflush(tracefile);
907 /* leave open because libedit might be using it */
908 return;
909 }
910 strcpy(s, "./trace");
911 if (tracefile) {
912 if (!freopen(s, "a", tracefile)) {
913 fprintf(stderr, "Can't re-open %s\n", s);
914 debug = 0;
915 return;
916 }
917 } else {
918 tracefile = fopen(s, "a");
919 if (tracefile == NULL) {
920 fprintf(stderr, "Can't open %s\n", s);
921 debug = 0;
922 return;
923 }
924 }
925#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000926 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000927 if (flags >= 0)
928 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
929#endif
930 setlinebuf(tracefile);
931 fputs("\nTracing started.\n", tracefile);
932}
933
934static void
935indent(int amount, char *pfx, FILE *fp)
936{
937 int i;
938
939 for (i = 0; i < amount; i++) {
940 if (pfx && i == amount - 1)
941 fputs(pfx, fp);
942 putc('\t', fp);
943 }
944}
945
946/* little circular references here... */
947static void shtree(union node *n, int ind, char *pfx, FILE *fp);
948
949static void
950sharg(union node *arg, FILE *fp)
951{
952 char *p;
953 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100954 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000955
956 if (arg->type != NARG) {
957 out1fmt("<node type %d>\n", arg->type);
958 abort();
959 }
960 bqlist = arg->narg.backquote;
961 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100962 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700964 p++;
965 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000966 break;
967 case CTLVAR:
968 putc('$', fp);
969 putc('{', fp);
970 subtype = *++p;
971 if (subtype == VSLENGTH)
972 putc('#', fp);
973
Dan Fandrich77d48722010-09-07 23:38:28 -0700974 while (*p != '=') {
975 putc(*p, fp);
976 p++;
977 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000978
979 if (subtype & VSNUL)
980 putc(':', fp);
981
982 switch (subtype & VSTYPE) {
983 case VSNORMAL:
984 putc('}', fp);
985 break;
986 case VSMINUS:
987 putc('-', fp);
988 break;
989 case VSPLUS:
990 putc('+', fp);
991 break;
992 case VSQUESTION:
993 putc('?', fp);
994 break;
995 case VSASSIGN:
996 putc('=', fp);
997 break;
998 case VSTRIMLEFT:
999 putc('#', fp);
1000 break;
1001 case VSTRIMLEFTMAX:
1002 putc('#', fp);
1003 putc('#', fp);
1004 break;
1005 case VSTRIMRIGHT:
1006 putc('%', fp);
1007 break;
1008 case VSTRIMRIGHTMAX:
1009 putc('%', fp);
1010 putc('%', fp);
1011 break;
1012 case VSLENGTH:
1013 break;
1014 default:
1015 out1fmt("<subtype %d>", subtype);
1016 }
1017 break;
1018 case CTLENDVAR:
1019 putc('}', fp);
1020 break;
1021 case CTLBACKQ:
1022 case CTLBACKQ|CTLQUOTE:
1023 putc('$', fp);
1024 putc('(', fp);
1025 shtree(bqlist->n, -1, NULL, fp);
1026 putc(')', fp);
1027 break;
1028 default:
1029 putc(*p, fp);
1030 break;
1031 }
1032 }
1033}
1034
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001035static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001036shcmd(union node *cmd, FILE *fp)
1037{
1038 union node *np;
1039 int first;
1040 const char *s;
1041 int dftfd;
1042
1043 first = 1;
1044 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001045 if (!first)
1046 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001047 sharg(np, fp);
1048 first = 0;
1049 }
1050 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001051 if (!first)
1052 putc(' ', fp);
1053 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001054 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001055 case NTO: s = ">>"+1; dftfd = 1; break;
1056 case NCLOBBER: s = ">|"; dftfd = 1; break;
1057 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001058#if ENABLE_ASH_BASH_COMPAT
1059 case NTO2:
1060#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001061 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001062 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001063 case NFROMFD: s = "<&"; break;
1064 case NFROMTO: s = "<>"; break;
1065 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001066 }
1067 if (np->nfile.fd != dftfd)
1068 fprintf(fp, "%d", np->nfile.fd);
1069 fputs(s, fp);
1070 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1071 fprintf(fp, "%d", np->ndup.dupfd);
1072 } else {
1073 sharg(np->nfile.fname, fp);
1074 }
1075 first = 0;
1076 }
1077}
1078
1079static void
1080shtree(union node *n, int ind, char *pfx, FILE *fp)
1081{
1082 struct nodelist *lp;
1083 const char *s;
1084
1085 if (n == NULL)
1086 return;
1087
1088 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001089
1090 if (n == NODE_EOF) {
1091 fputs("<EOF>", fp);
1092 return;
1093 }
1094
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001095 switch (n->type) {
1096 case NSEMI:
1097 s = "; ";
1098 goto binop;
1099 case NAND:
1100 s = " && ";
1101 goto binop;
1102 case NOR:
1103 s = " || ";
1104 binop:
1105 shtree(n->nbinary.ch1, ind, NULL, fp);
1106 /* if (ind < 0) */
1107 fputs(s, fp);
1108 shtree(n->nbinary.ch2, ind, NULL, fp);
1109 break;
1110 case NCMD:
1111 shcmd(n, fp);
1112 if (ind >= 0)
1113 putc('\n', fp);
1114 break;
1115 case NPIPE:
1116 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001117 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001118 if (lp->next)
1119 fputs(" | ", fp);
1120 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001121 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001122 fputs(" &", fp);
1123 if (ind >= 0)
1124 putc('\n', fp);
1125 break;
1126 default:
1127 fprintf(fp, "<node type %d>", n->type);
1128 if (ind >= 0)
1129 putc('\n', fp);
1130 break;
1131 }
1132}
1133
1134static void
1135showtree(union node *n)
1136{
1137 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001138 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001139}
1140
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001141#endif /* DEBUG */
1142
1143
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001144/* ============ Parser data */
1145
1146/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001147 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1148 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001149struct strlist {
1150 struct strlist *next;
1151 char *text;
1152};
1153
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001154struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001155
Denis Vlasenkob012b102007-02-19 22:43:01 +00001156struct strpush {
1157 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001158 char *prev_string;
1159 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001160#if ENABLE_ASH_ALIAS
1161 struct alias *ap; /* if push was associated with an alias */
1162#endif
1163 char *string; /* remember the string since it may change */
1164};
1165
1166struct parsefile {
1167 struct parsefile *prev; /* preceding file on stack */
1168 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001169 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001170 int left_in_line; /* number of chars left in this line */
1171 int left_in_buffer; /* number of chars left in this buffer past the line */
1172 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001173 char *buf; /* input buffer */
1174 struct strpush *strpush; /* for pushing strings at this level */
1175 struct strpush basestrpush; /* so pushing one is fast */
1176};
1177
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001178static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001179static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001180static int startlinno; /* line # where last token started */
1181static char *commandname; /* currently executing command */
1182static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001183static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001184
1185
1186/* ============ Message printing */
1187
1188static void
1189ash_vmsg(const char *msg, va_list ap)
1190{
1191 fprintf(stderr, "%s: ", arg0);
1192 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001193 if (strcmp(arg0, commandname))
1194 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001195 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001196 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001197 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001198 vfprintf(stderr, msg, ap);
1199 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001200}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001201
1202/*
1203 * Exverror is called to raise the error exception. If the second argument
1204 * is not NULL then error prints an error message using printf style
1205 * formatting. It then raises the error exception.
1206 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001207static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001208static void
1209ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001210{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001211#if DEBUG
1212 if (msg) {
1213 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1214 TRACEV((msg, ap));
1215 TRACE(("\") pid=%d\n", getpid()));
1216 } else
1217 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1218 if (msg)
1219#endif
1220 ash_vmsg(msg, ap);
1221
1222 flush_stdout_stderr();
1223 raise_exception(cond);
1224 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001225}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001226
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001227static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001228static void
1229ash_msg_and_raise_error(const char *msg, ...)
1230{
1231 va_list ap;
1232
1233 va_start(ap, msg);
1234 ash_vmsg_and_raise(EXERROR, msg, ap);
1235 /* NOTREACHED */
1236 va_end(ap);
1237}
1238
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001239static void raise_error_syntax(const char *) NORETURN;
1240static void
1241raise_error_syntax(const char *msg)
1242{
1243 ash_msg_and_raise_error("syntax error: %s", msg);
1244 /* NOTREACHED */
1245}
1246
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001247static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001248static void
1249ash_msg_and_raise(int cond, const char *msg, ...)
1250{
1251 va_list ap;
1252
1253 va_start(ap, msg);
1254 ash_vmsg_and_raise(cond, msg, ap);
1255 /* NOTREACHED */
1256 va_end(ap);
1257}
1258
1259/*
1260 * error/warning routines for external builtins
1261 */
1262static void
1263ash_msg(const char *fmt, ...)
1264{
1265 va_list ap;
1266
1267 va_start(ap, fmt);
1268 ash_vmsg(fmt, ap);
1269 va_end(ap);
1270}
1271
1272/*
1273 * Return a string describing an error. The returned string may be a
1274 * pointer to a static buffer that will be overwritten on the next call.
1275 * Action describes the operation that got the error.
1276 */
1277static const char *
1278errmsg(int e, const char *em)
1279{
1280 if (e == ENOENT || e == ENOTDIR) {
1281 return em;
1282 }
1283 return strerror(e);
1284}
1285
1286
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001287/* ============ Memory allocation */
1288
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001289#if 0
1290/* I consider these wrappers nearly useless:
1291 * ok, they return you to nearest exception handler, but
1292 * how much memory do you leak in the process, making
1293 * memory starvation worse?
1294 */
1295static void *
1296ckrealloc(void * p, size_t nbytes)
1297{
1298 p = realloc(p, nbytes);
1299 if (!p)
1300 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1301 return p;
1302}
1303
1304static void *
1305ckmalloc(size_t nbytes)
1306{
1307 return ckrealloc(NULL, nbytes);
1308}
1309
1310static void *
1311ckzalloc(size_t nbytes)
1312{
1313 return memset(ckmalloc(nbytes), 0, nbytes);
1314}
1315
1316static char *
1317ckstrdup(const char *s)
1318{
1319 char *p = strdup(s);
1320 if (!p)
1321 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1322 return p;
1323}
1324#else
1325/* Using bbox equivalents. They exit if out of memory */
1326# define ckrealloc xrealloc
1327# define ckmalloc xmalloc
1328# define ckzalloc xzalloc
1329# define ckstrdup xstrdup
1330#endif
1331
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001332/*
1333 * It appears that grabstackstr() will barf with such alignments
1334 * because stalloc() will return a string allocated in a new stackblock.
1335 */
1336#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1337enum {
1338 /* Most machines require the value returned from malloc to be aligned
1339 * in some way. The following macro will get this right
1340 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001341 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001342 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001343 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001344};
1345
1346struct stack_block {
1347 struct stack_block *prev;
1348 char space[MINSIZE];
1349};
1350
1351struct stackmark {
1352 struct stack_block *stackp;
1353 char *stacknxt;
1354 size_t stacknleft;
1355 struct stackmark *marknext;
1356};
1357
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001358
Denis Vlasenko01631112007-12-16 17:20:38 +00001359struct globals_memstack {
1360 struct stack_block *g_stackp; // = &stackbase;
1361 struct stackmark *markp;
1362 char *g_stacknxt; // = stackbase.space;
1363 char *sstrend; // = stackbase.space + MINSIZE;
1364 size_t g_stacknleft; // = MINSIZE;
1365 int herefd; // = -1;
1366 struct stack_block stackbase;
1367};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001368extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1369#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001370#define g_stackp (G_memstack.g_stackp )
1371#define markp (G_memstack.markp )
1372#define g_stacknxt (G_memstack.g_stacknxt )
1373#define sstrend (G_memstack.sstrend )
1374#define g_stacknleft (G_memstack.g_stacknleft)
1375#define herefd (G_memstack.herefd )
1376#define stackbase (G_memstack.stackbase )
1377#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001378 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1379 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001380 g_stackp = &stackbase; \
1381 g_stacknxt = stackbase.space; \
1382 g_stacknleft = MINSIZE; \
1383 sstrend = stackbase.space + MINSIZE; \
1384 herefd = -1; \
1385} while (0)
1386
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001387
Denis Vlasenko01631112007-12-16 17:20:38 +00001388#define stackblock() ((void *)g_stacknxt)
1389#define stackblocksize() g_stacknleft
1390
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001391/*
1392 * Parse trees for commands are allocated in lifo order, so we use a stack
1393 * to make this more efficient, and also to avoid all sorts of exception
1394 * handling code to handle interrupts in the middle of a parse.
1395 *
1396 * The size 504 was chosen because the Ultrix malloc handles that size
1397 * well.
1398 */
1399static void *
1400stalloc(size_t nbytes)
1401{
1402 char *p;
1403 size_t aligned;
1404
1405 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001406 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001407 size_t len;
1408 size_t blocksize;
1409 struct stack_block *sp;
1410
1411 blocksize = aligned;
1412 if (blocksize < MINSIZE)
1413 blocksize = MINSIZE;
1414 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1415 if (len < blocksize)
1416 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1417 INT_OFF;
1418 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001419 sp->prev = g_stackp;
1420 g_stacknxt = sp->space;
1421 g_stacknleft = blocksize;
1422 sstrend = g_stacknxt + blocksize;
1423 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001424 INT_ON;
1425 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001426 p = g_stacknxt;
1427 g_stacknxt += aligned;
1428 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001429 return p;
1430}
1431
Denis Vlasenko597906c2008-02-20 16:38:54 +00001432static void *
1433stzalloc(size_t nbytes)
1434{
1435 return memset(stalloc(nbytes), 0, nbytes);
1436}
1437
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438static void
1439stunalloc(void *p)
1440{
1441#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001442 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001443 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001444 abort();
1445 }
1446#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001447 g_stacknleft += g_stacknxt - (char *)p;
1448 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001449}
1450
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001451/*
1452 * Like strdup but works with the ash stack.
1453 */
1454static char *
1455ststrdup(const char *p)
1456{
1457 size_t len = strlen(p) + 1;
1458 return memcpy(stalloc(len), p, len);
1459}
1460
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001461static void
1462setstackmark(struct stackmark *mark)
1463{
Denis Vlasenko01631112007-12-16 17:20:38 +00001464 mark->stackp = g_stackp;
1465 mark->stacknxt = g_stacknxt;
1466 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001467 mark->marknext = markp;
1468 markp = mark;
1469}
1470
1471static void
1472popstackmark(struct stackmark *mark)
1473{
1474 struct stack_block *sp;
1475
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001476 if (!mark->stackp)
1477 return;
1478
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001479 INT_OFF;
1480 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001481 while (g_stackp != mark->stackp) {
1482 sp = g_stackp;
1483 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001484 free(sp);
1485 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001486 g_stacknxt = mark->stacknxt;
1487 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001488 sstrend = mark->stacknxt + mark->stacknleft;
1489 INT_ON;
1490}
1491
1492/*
1493 * When the parser reads in a string, it wants to stick the string on the
1494 * stack and only adjust the stack pointer when it knows how big the
1495 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1496 * of space on top of the stack and stackblocklen returns the length of
1497 * this block. Growstackblock will grow this space by at least one byte,
1498 * possibly moving it (like realloc). Grabstackblock actually allocates the
1499 * part of the block that has been used.
1500 */
1501static void
1502growstackblock(void)
1503{
1504 size_t newlen;
1505
Denis Vlasenko01631112007-12-16 17:20:38 +00001506 newlen = g_stacknleft * 2;
1507 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001508 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1509 if (newlen < 128)
1510 newlen += 128;
1511
Denis Vlasenko01631112007-12-16 17:20:38 +00001512 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001513 struct stack_block *oldstackp;
1514 struct stackmark *xmark;
1515 struct stack_block *sp;
1516 struct stack_block *prevstackp;
1517 size_t grosslen;
1518
1519 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001520 oldstackp = g_stackp;
1521 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001522 prevstackp = sp->prev;
1523 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1524 sp = ckrealloc(sp, grosslen);
1525 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001526 g_stackp = sp;
1527 g_stacknxt = sp->space;
1528 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001529 sstrend = sp->space + newlen;
1530
1531 /*
1532 * Stack marks pointing to the start of the old block
1533 * must be relocated to point to the new block
1534 */
1535 xmark = markp;
1536 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001537 xmark->stackp = g_stackp;
1538 xmark->stacknxt = g_stacknxt;
1539 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001540 xmark = xmark->marknext;
1541 }
1542 INT_ON;
1543 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001544 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001545 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001546 char *p = stalloc(newlen);
1547
1548 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001549 g_stacknxt = memcpy(p, oldspace, oldlen);
1550 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001551 }
1552}
1553
1554static void
1555grabstackblock(size_t len)
1556{
1557 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001558 g_stacknxt += len;
1559 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001560}
1561
1562/*
1563 * The following routines are somewhat easier to use than the above.
1564 * The user declares a variable of type STACKSTR, which may be declared
1565 * to be a register. The macro STARTSTACKSTR initializes things. Then
1566 * the user uses the macro STPUTC to add characters to the string. In
1567 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1568 * grown as necessary. When the user is done, she can just leave the
1569 * string there and refer to it using stackblock(). Or she can allocate
1570 * the space for it using grabstackstr(). If it is necessary to allow
1571 * someone else to use the stack temporarily and then continue to grow
1572 * the string, the user should use grabstack to allocate the space, and
1573 * then call ungrabstr(p) to return to the previous mode of operation.
1574 *
1575 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1576 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1577 * is space for at least one character.
1578 */
1579static void *
1580growstackstr(void)
1581{
1582 size_t len = stackblocksize();
1583 if (herefd >= 0 && len >= 1024) {
1584 full_write(herefd, stackblock(), len);
1585 return stackblock();
1586 }
1587 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001588 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001589}
1590
1591/*
1592 * Called from CHECKSTRSPACE.
1593 */
1594static char *
1595makestrspace(size_t newlen, char *p)
1596{
Denis Vlasenko01631112007-12-16 17:20:38 +00001597 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001598 size_t size = stackblocksize();
1599
1600 for (;;) {
1601 size_t nleft;
1602
1603 size = stackblocksize();
1604 nleft = size - len;
1605 if (nleft >= newlen)
1606 break;
1607 growstackblock();
1608 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001609 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001610}
1611
1612static char *
1613stack_nputstr(const char *s, size_t n, char *p)
1614{
1615 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001616 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001617 return p;
1618}
1619
1620static char *
1621stack_putstr(const char *s, char *p)
1622{
1623 return stack_nputstr(s, strlen(s), p);
1624}
1625
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001626static char *
1627_STPUTC(int c, char *p)
1628{
1629 if (p == sstrend)
1630 p = growstackstr();
1631 *p++ = c;
1632 return p;
1633}
1634
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001635#define STARTSTACKSTR(p) ((p) = stackblock())
1636#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001637#define CHECKSTRSPACE(n, p) do { \
1638 char *q = (p); \
1639 size_t l = (n); \
1640 size_t m = sstrend - q; \
1641 if (l > m) \
1642 (p) = makestrspace(l, q); \
1643} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001644#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001645#define STACKSTRNUL(p) do { \
1646 if ((p) == sstrend) \
1647 (p) = growstackstr(); \
1648 *(p) = '\0'; \
1649} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001650#define STUNPUTC(p) (--(p))
1651#define STTOPC(p) ((p)[-1])
1652#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001653
1654#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001655#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001656#define stackstrend() ((void *)sstrend)
1657
1658
1659/* ============ String helpers */
1660
1661/*
1662 * prefix -- see if pfx is a prefix of string.
1663 */
1664static char *
1665prefix(const char *string, const char *pfx)
1666{
1667 while (*pfx) {
1668 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001669 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001670 }
1671 return (char *) string;
1672}
1673
1674/*
1675 * Check for a valid number. This should be elsewhere.
1676 */
1677static int
1678is_number(const char *p)
1679{
1680 do {
1681 if (!isdigit(*p))
1682 return 0;
1683 } while (*++p != '\0');
1684 return 1;
1685}
1686
1687/*
1688 * Convert a string of digits to an integer, printing an error message on
1689 * failure.
1690 */
1691static int
1692number(const char *s)
1693{
1694 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001695 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001696 return atoi(s);
1697}
1698
1699/*
1700 * Produce a possibly single quoted string suitable as input to the shell.
1701 * The return string is allocated on the stack.
1702 */
1703static char *
1704single_quote(const char *s)
1705{
1706 char *p;
1707
1708 STARTSTACKSTR(p);
1709
1710 do {
1711 char *q;
1712 size_t len;
1713
1714 len = strchrnul(s, '\'') - s;
1715
1716 q = p = makestrspace(len + 3, p);
1717
1718 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001719 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720 *q++ = '\'';
1721 s += len;
1722
1723 STADJUST(q - p, p);
1724
Denys Vlasenkocd716832009-11-28 22:14:02 +01001725 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001726 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001727 len = 0;
1728 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729
1730 q = p = makestrspace(len + 3, p);
1731
1732 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001733 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001735
1736 STADJUST(q - p, p);
1737 } while (*s);
1738
Denys Vlasenkocd716832009-11-28 22:14:02 +01001739 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740
1741 return stackblock();
1742}
1743
1744
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001745/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001746
1747static char **argptr; /* argument list for builtin commands */
1748static char *optionarg; /* set by nextopt (like getopt) */
1749static char *optptr; /* used by nextopt */
1750
1751/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001752 * XXX - should get rid of. Have all builtins use getopt(3).
1753 * The library getopt must have the BSD extension static variable
1754 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001755 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001756 * Standard option processing (a la getopt) for builtin routines.
1757 * The only argument that is passed to nextopt is the option string;
1758 * the other arguments are unnecessary. It returns the character,
1759 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001760 */
1761static int
1762nextopt(const char *optstring)
1763{
1764 char *p;
1765 const char *q;
1766 char c;
1767
1768 p = optptr;
1769 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001770 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001772 if (p == NULL)
1773 return '\0';
1774 if (*p != '-')
1775 return '\0';
1776 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001777 return '\0';
1778 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001779 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001781 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001783 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001784 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001785 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001786 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001787 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001788 if (*++q == ':')
1789 q++;
1790 }
1791 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001792 if (*p == '\0') {
1793 p = *argptr++;
1794 if (p == NULL)
1795 ash_msg_and_raise_error("no arg for -%c option", c);
1796 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001797 optionarg = p;
1798 p = NULL;
1799 }
1800 optptr = p;
1801 return c;
1802}
1803
1804
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001805/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001806
Denis Vlasenko01631112007-12-16 17:20:38 +00001807/*
1808 * The parsefile structure pointed to by the global variable parsefile
1809 * contains information about the current file being read.
1810 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001811struct shparam {
1812 int nparam; /* # of positional parameters (without $0) */
1813#if ENABLE_ASH_GETOPTS
1814 int optind; /* next parameter to be processed by getopts */
1815 int optoff; /* used by getopts */
1816#endif
1817 unsigned char malloced; /* if parameter list dynamically allocated */
1818 char **p; /* parameter list */
1819};
1820
1821/*
1822 * Free the list of positional parameters.
1823 */
1824static void
1825freeparam(volatile struct shparam *param)
1826{
Denis Vlasenko01631112007-12-16 17:20:38 +00001827 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001828 char **ap, **ap1;
1829 ap = ap1 = param->p;
1830 while (*ap)
1831 free(*ap++);
1832 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001833 }
1834}
1835
1836#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001837static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001838#endif
1839
1840struct var {
1841 struct var *next; /* next entry in hash list */
1842 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001843 const char *var_text; /* name=value */
1844 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001845 /* the variable gets set/unset */
1846};
1847
1848struct localvar {
1849 struct localvar *next; /* next local variable in list */
1850 struct var *vp; /* the variable that was made local */
1851 int flags; /* saved flags */
1852 const char *text; /* saved text */
1853};
1854
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001855/* flags */
1856#define VEXPORT 0x01 /* variable is exported */
1857#define VREADONLY 0x02 /* variable cannot be modified */
1858#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1859#define VTEXTFIXED 0x08 /* text is statically allocated */
1860#define VSTACK 0x10 /* text is allocated on the stack */
1861#define VUNSET 0x20 /* the variable is not set */
1862#define VNOFUNC 0x40 /* don't call the callback function */
1863#define VNOSET 0x80 /* do not set variable - just readonly test */
1864#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001865#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001866# define VDYNAMIC 0x200 /* dynamic variable */
1867#else
1868# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001869#endif
1870
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001871
Denis Vlasenko01631112007-12-16 17:20:38 +00001872/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001873#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001874static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001875change_lc_all(const char *value)
1876{
1877 if (value && *value != '\0')
1878 setlocale(LC_ALL, value);
1879}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001880static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001881change_lc_ctype(const char *value)
1882{
1883 if (value && *value != '\0')
1884 setlocale(LC_CTYPE, value);
1885}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001886#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887#if ENABLE_ASH_MAIL
1888static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001889static void changemail(const char *var_value) FAST_FUNC;
1890#else
1891# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001893static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001894#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001895static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001896#endif
1897
Denis Vlasenko01631112007-12-16 17:20:38 +00001898static const struct {
1899 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001900 const char *var_text;
1901 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001902} varinit_data[] = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02001903 /*
1904 * Note: VEXPORT would not work correctly here for NOFORK applets:
1905 * some environment strings may be constant.
1906 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001907 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001909 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1910 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001912 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1913 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1914 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1915 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001916#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001917 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918#endif
1919#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001920 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921#endif
1922#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001923 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1924 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001925#endif
1926#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001927 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001928#endif
1929};
1930
Denis Vlasenko0b769642008-07-24 07:54:57 +00001931struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001932
1933struct globals_var {
1934 struct shparam shellparam; /* $@ current positional parameters */
1935 struct redirtab *redirlist;
1936 int g_nullredirs;
1937 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1938 struct var *vartab[VTABSIZE];
1939 struct var varinit[ARRAY_SIZE(varinit_data)];
1940};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001941extern struct globals_var *const ash_ptr_to_globals_var;
1942#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001943#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001944//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001945#define g_nullredirs (G_var.g_nullredirs )
1946#define preverrout_fd (G_var.preverrout_fd)
1947#define vartab (G_var.vartab )
1948#define varinit (G_var.varinit )
1949#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001950 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001951 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1952 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001953 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001954 varinit[i].flags = varinit_data[i].flags; \
1955 varinit[i].var_text = varinit_data[i].var_text; \
1956 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001957 } \
1958} while (0)
1959
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001960#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001961#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001962# define vmail (&vifs)[1]
1963# define vmpath (&vmail)[1]
1964# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001965#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001966# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001967#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001968#define vps1 (&vpath)[1]
1969#define vps2 (&vps1)[1]
1970#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001972# define voptind (&vps4)[1]
1973# if ENABLE_ASH_RANDOM_SUPPORT
1974# define vrandom (&voptind)[1]
1975# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001977# if ENABLE_ASH_RANDOM_SUPPORT
1978# define vrandom (&vps4)[1]
1979# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001981
1982/*
1983 * The following macros access the values of the above variables.
1984 * They have to skip over the name. They return the null string
1985 * for unset variables.
1986 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001987#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001988#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001989#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001990# define mailval() (vmail.var_text + 5)
1991# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001992# define mpathset() ((vmpath.flags & VUNSET) == 0)
1993#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001994#define pathval() (vpath.var_text + 5)
1995#define ps1val() (vps1.var_text + 4)
1996#define ps2val() (vps2.var_text + 4)
1997#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001998#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001999# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002000#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002001
Denis Vlasenko01631112007-12-16 17:20:38 +00002002#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002003static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002004getoptsreset(const char *value)
2005{
2006 shellparam.optind = number(value);
2007 shellparam.optoff = -1;
2008}
2009#endif
2010
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002011/* math.h has these, otherwise define our private copies */
2012#if !ENABLE_SH_MATH_SUPPORT
2013#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2014#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002016 * Return the pointer to the first char which is not part of a legal variable name
2017 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002018 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002019static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002020endofname(const char *name)
2021{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002022 if (!is_name(*name))
2023 return name;
2024 while (*++name) {
2025 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002026 break;
2027 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002028 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002029}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002030#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002031
2032/*
2033 * Compares two strings up to the first = or '\0'. The first
2034 * string must be terminated by '='; the second may be terminated by
2035 * either '=' or '\0'.
2036 */
2037static int
2038varcmp(const char *p, const char *q)
2039{
2040 int c, d;
2041
2042 while ((c = *p) == (d = *q)) {
2043 if (!c || c == '=')
2044 goto out;
2045 p++;
2046 q++;
2047 }
2048 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002049 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002050 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002051 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002052 out:
2053 return c - d;
2054}
2055
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002056/*
2057 * Find the appropriate entry in the hash table from the name.
2058 */
2059static struct var **
2060hashvar(const char *p)
2061{
2062 unsigned hashval;
2063
2064 hashval = ((unsigned char) *p) << 4;
2065 while (*p && *p != '=')
2066 hashval += (unsigned char) *p++;
2067 return &vartab[hashval % VTABSIZE];
2068}
2069
2070static int
2071vpcmp(const void *a, const void *b)
2072{
2073 return varcmp(*(const char **)a, *(const char **)b);
2074}
2075
2076/*
2077 * This routine initializes the builtin variables.
2078 */
2079static void
2080initvar(void)
2081{
2082 struct var *vp;
2083 struct var *end;
2084 struct var **vpp;
2085
2086 /*
2087 * PS1 depends on uid
2088 */
2089#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002090 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002091#else
2092 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002093 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094#endif
2095 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002096 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002097 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002098 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002099 vp->next = *vpp;
2100 *vpp = vp;
2101 } while (++vp < end);
2102}
2103
2104static struct var **
2105findvar(struct var **vpp, const char *name)
2106{
2107 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002108 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002109 break;
2110 }
2111 }
2112 return vpp;
2113}
2114
2115/*
2116 * Find the value of a variable. Returns NULL if not set.
2117 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002118static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119lookupvar(const char *name)
2120{
2121 struct var *v;
2122
2123 v = *findvar(hashvar(name), name);
2124 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002125#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126 /*
2127 * Dynamic variables are implemented roughly the same way they are
2128 * in bash. Namely, they're "special" so long as they aren't unset.
2129 * As soon as they're unset, they're no longer dynamic, and dynamic
2130 * lookup will no longer happen at that point. -- PFM.
2131 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002132 if (v->flags & VDYNAMIC)
2133 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002134#endif
2135 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002136 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137 }
2138 return NULL;
2139}
2140
2141/*
2142 * Search the environment of a builtin command.
2143 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002144static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145bltinlookup(const char *name)
2146{
2147 struct strlist *sp;
2148
2149 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002150 if (varcmp(sp->text, name) == 0)
2151 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002152 }
2153 return lookupvar(name);
2154}
2155
2156/*
2157 * Same as setvar except that the variable and value are passed in
2158 * the first argument as name=value. Since the first argument will
2159 * be actually stored in the table, it should not be a string that
2160 * will go away.
2161 * Called with interrupts off.
2162 */
2163static void
2164setvareq(char *s, int flags)
2165{
2166 struct var *vp, **vpp;
2167
2168 vpp = hashvar(s);
2169 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2170 vp = *findvar(vpp, s);
2171 if (vp) {
2172 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2173 const char *n;
2174
2175 if (flags & VNOSAVE)
2176 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002178 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2179 }
2180
2181 if (flags & VNOSET)
2182 return;
2183
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002184 if (vp->var_func && !(flags & VNOFUNC))
2185 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002187 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2188 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002189
2190 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2191 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002192 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193 if (flags & VNOSET)
2194 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002195 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002196 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002197 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002198 *vpp = vp;
2199 }
2200 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2201 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002202 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002203 vp->flags = flags;
2204}
2205
2206/*
2207 * Set the value of a variable. The flags argument is ored with the
2208 * flags of the variable. If val is NULL, the variable is unset.
2209 */
2210static void
2211setvar(const char *name, const char *val, int flags)
2212{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002213 const char *q;
2214 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002215 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002216 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002217 size_t vallen;
2218
2219 q = endofname(name);
2220 p = strchrnul(q, '=');
2221 namelen = p - name;
2222 if (!namelen || p != q)
2223 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2224 vallen = 0;
2225 if (val == NULL) {
2226 flags |= VUNSET;
2227 } else {
2228 vallen = strlen(val);
2229 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002230
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231 INT_OFF;
2232 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002233 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002234 if (val) {
2235 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002236 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002237 }
2238 *p = '\0';
2239 setvareq(nameeq, flags | VNOSAVE);
2240 INT_ON;
2241}
2242
Denys Vlasenko03dad222010-01-12 23:29:57 +01002243static void FAST_FUNC
2244setvar2(const char *name, const char *val)
2245{
2246 setvar(name, val, 0);
2247}
2248
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002249#if ENABLE_ASH_GETOPTS
2250/*
2251 * Safe version of setvar, returns 1 on success 0 on failure.
2252 */
2253static int
2254setvarsafe(const char *name, const char *val, int flags)
2255{
2256 int err;
2257 volatile int saveint;
2258 struct jmploc *volatile savehandler = exception_handler;
2259 struct jmploc jmploc;
2260
2261 SAVE_INT(saveint);
2262 if (setjmp(jmploc.loc))
2263 err = 1;
2264 else {
2265 exception_handler = &jmploc;
2266 setvar(name, val, flags);
2267 err = 0;
2268 }
2269 exception_handler = savehandler;
2270 RESTORE_INT(saveint);
2271 return err;
2272}
2273#endif
2274
2275/*
2276 * Unset the specified variable.
2277 */
2278static int
2279unsetvar(const char *s)
2280{
2281 struct var **vpp;
2282 struct var *vp;
2283 int retval;
2284
2285 vpp = findvar(hashvar(s), s);
2286 vp = *vpp;
2287 retval = 2;
2288 if (vp) {
2289 int flags = vp->flags;
2290
2291 retval = 1;
2292 if (flags & VREADONLY)
2293 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002294#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002295 vp->flags &= ~VDYNAMIC;
2296#endif
2297 if (flags & VUNSET)
2298 goto ok;
2299 if ((flags & VSTRFIXED) == 0) {
2300 INT_OFF;
2301 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002302 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002303 *vpp = vp->next;
2304 free(vp);
2305 INT_ON;
2306 } else {
2307 setvar(s, 0, 0);
2308 vp->flags &= ~VEXPORT;
2309 }
2310 ok:
2311 retval = 0;
2312 }
2313 out:
2314 return retval;
2315}
2316
2317/*
2318 * Process a linked list of variable assignments.
2319 */
2320static void
2321listsetvar(struct strlist *list_set_var, int flags)
2322{
2323 struct strlist *lp = list_set_var;
2324
2325 if (!lp)
2326 return;
2327 INT_OFF;
2328 do {
2329 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002330 lp = lp->next;
2331 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002332 INT_ON;
2333}
2334
2335/*
2336 * Generate a list of variables satisfying the given conditions.
2337 */
2338static char **
2339listvars(int on, int off, char ***end)
2340{
2341 struct var **vpp;
2342 struct var *vp;
2343 char **ep;
2344 int mask;
2345
2346 STARTSTACKSTR(ep);
2347 vpp = vartab;
2348 mask = on | off;
2349 do {
2350 for (vp = *vpp; vp; vp = vp->next) {
2351 if ((vp->flags & mask) == on) {
2352 if (ep == stackstrend())
2353 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002354 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002355 }
2356 }
2357 } while (++vpp < vartab + VTABSIZE);
2358 if (ep == stackstrend())
2359 ep = growstackstr();
2360 if (end)
2361 *end = ep;
2362 *ep++ = NULL;
2363 return grabstackstr(ep);
2364}
2365
2366
2367/* ============ Path search helper
2368 *
2369 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002370 * of the path before the first call; path_advance will update
2371 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002372 * the possible path expansions in sequence. If an option (indicated by
2373 * a percent sign) appears in the path entry then the global variable
2374 * pathopt will be set to point to it; otherwise pathopt will be set to
2375 * NULL.
2376 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002377static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378
2379static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002380path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002381{
2382 const char *p;
2383 char *q;
2384 const char *start;
2385 size_t len;
2386
2387 if (*path == NULL)
2388 return NULL;
2389 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002390 for (p = start; *p && *p != ':' && *p != '%'; p++)
2391 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002392 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2393 while (stackblocksize() < len)
2394 growstackblock();
2395 q = stackblock();
2396 if (p != start) {
2397 memcpy(q, start, p - start);
2398 q += p - start;
2399 *q++ = '/';
2400 }
2401 strcpy(q, name);
2402 pathopt = NULL;
2403 if (*p == '%') {
2404 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002405 while (*p && *p != ':')
2406 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407 }
2408 if (*p == ':')
2409 *path = p + 1;
2410 else
2411 *path = NULL;
2412 return stalloc(len);
2413}
2414
2415
2416/* ============ Prompt */
2417
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002418static smallint doprompt; /* if set, prompt the user */
2419static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002420
2421#if ENABLE_FEATURE_EDITING
2422static line_input_t *line_input_state;
2423static const char *cmdedit_prompt;
2424static void
2425putprompt(const char *s)
2426{
2427 if (ENABLE_ASH_EXPAND_PRMT) {
2428 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002429 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002430 return;
2431 }
2432 cmdedit_prompt = s;
2433}
2434#else
2435static void
2436putprompt(const char *s)
2437{
2438 out2str(s);
2439}
2440#endif
2441
2442#if ENABLE_ASH_EXPAND_PRMT
2443/* expandstr() needs parsing machinery, so it is far away ahead... */
2444static const char *expandstr(const char *ps);
2445#else
2446#define expandstr(s) s
2447#endif
2448
2449static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002450setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002451{
2452 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002453 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2454
2455 if (!do_set)
2456 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002457
2458 needprompt = 0;
2459
2460 switch (whichprompt) {
2461 case 1:
2462 prompt = ps1val();
2463 break;
2464 case 2:
2465 prompt = ps2val();
2466 break;
2467 default: /* 0 */
2468 prompt = nullstr;
2469 }
2470#if ENABLE_ASH_EXPAND_PRMT
2471 setstackmark(&smark);
2472 stalloc(stackblocksize());
2473#endif
2474 putprompt(expandstr(prompt));
2475#if ENABLE_ASH_EXPAND_PRMT
2476 popstackmark(&smark);
2477#endif
2478}
2479
2480
2481/* ============ The cd and pwd commands */
2482
2483#define CD_PHYSICAL 1
2484#define CD_PRINT 2
2485
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002486static int
2487cdopt(void)
2488{
2489 int flags = 0;
2490 int i, j;
2491
2492 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002493 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494 if (i != j) {
2495 flags ^= CD_PHYSICAL;
2496 j = i;
2497 }
2498 }
2499
2500 return flags;
2501}
2502
2503/*
2504 * Update curdir (the name of the current directory) in response to a
2505 * cd command.
2506 */
2507static const char *
2508updatepwd(const char *dir)
2509{
2510 char *new;
2511 char *p;
2512 char *cdcomppath;
2513 const char *lim;
2514
2515 cdcomppath = ststrdup(dir);
2516 STARTSTACKSTR(new);
2517 if (*dir != '/') {
2518 if (curdir == nullstr)
2519 return 0;
2520 new = stack_putstr(curdir, new);
2521 }
2522 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002523 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002524 if (*dir != '/') {
2525 if (new[-1] != '/')
2526 USTPUTC('/', new);
2527 if (new > lim && *lim == '/')
2528 lim++;
2529 } else {
2530 USTPUTC('/', new);
2531 cdcomppath++;
2532 if (dir[1] == '/' && dir[2] != '/') {
2533 USTPUTC('/', new);
2534 cdcomppath++;
2535 lim++;
2536 }
2537 }
2538 p = strtok(cdcomppath, "/");
2539 while (p) {
2540 switch (*p) {
2541 case '.':
2542 if (p[1] == '.' && p[2] == '\0') {
2543 while (new > lim) {
2544 STUNPUTC(new);
2545 if (new[-1] == '/')
2546 break;
2547 }
2548 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002549 }
2550 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002551 break;
2552 /* fall through */
2553 default:
2554 new = stack_putstr(p, new);
2555 USTPUTC('/', new);
2556 }
2557 p = strtok(0, "/");
2558 }
2559 if (new > lim)
2560 STUNPUTC(new);
2561 *new = 0;
2562 return stackblock();
2563}
2564
2565/*
2566 * Find out what the current directory is. If we already know the current
2567 * directory, this routine returns immediately.
2568 */
2569static char *
2570getpwd(void)
2571{
Denis Vlasenko01631112007-12-16 17:20:38 +00002572 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002573 return dir ? dir : nullstr;
2574}
2575
2576static void
2577setpwd(const char *val, int setold)
2578{
2579 char *oldcur, *dir;
2580
2581 oldcur = dir = curdir;
2582
2583 if (setold) {
2584 setvar("OLDPWD", oldcur, VEXPORT);
2585 }
2586 INT_OFF;
2587 if (physdir != nullstr) {
2588 if (physdir != oldcur)
2589 free(physdir);
2590 physdir = nullstr;
2591 }
2592 if (oldcur == val || !val) {
2593 char *s = getpwd();
2594 physdir = s;
2595 if (!val)
2596 dir = s;
2597 } else
2598 dir = ckstrdup(val);
2599 if (oldcur != dir && oldcur != nullstr) {
2600 free(oldcur);
2601 }
2602 curdir = dir;
2603 INT_ON;
2604 setvar("PWD", dir, VEXPORT);
2605}
2606
2607static void hashcd(void);
2608
2609/*
2610 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2611 * know that the current directory has changed.
2612 */
2613static int
2614docd(const char *dest, int flags)
2615{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002616 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002617 int err;
2618
2619 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2620
2621 INT_OFF;
2622 if (!(flags & CD_PHYSICAL)) {
2623 dir = updatepwd(dest);
2624 if (dir)
2625 dest = dir;
2626 }
2627 err = chdir(dest);
2628 if (err)
2629 goto out;
2630 setpwd(dir, 1);
2631 hashcd();
2632 out:
2633 INT_ON;
2634 return err;
2635}
2636
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002637static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002638cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002639{
2640 const char *dest;
2641 const char *path;
2642 const char *p;
2643 char c;
2644 struct stat statb;
2645 int flags;
2646
2647 flags = cdopt();
2648 dest = *argptr;
2649 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002650 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002651 else if (LONE_DASH(dest)) {
2652 dest = bltinlookup("OLDPWD");
2653 flags |= CD_PRINT;
2654 }
2655 if (!dest)
2656 dest = nullstr;
2657 if (*dest == '/')
2658 goto step7;
2659 if (*dest == '.') {
2660 c = dest[1];
2661 dotdot:
2662 switch (c) {
2663 case '\0':
2664 case '/':
2665 goto step6;
2666 case '.':
2667 c = dest[2];
2668 if (c != '.')
2669 goto dotdot;
2670 }
2671 }
2672 if (!*dest)
2673 dest = ".";
2674 path = bltinlookup("CDPATH");
2675 if (!path) {
2676 step6:
2677 step7:
2678 p = dest;
2679 goto docd;
2680 }
2681 do {
2682 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002683 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002684 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2685 if (c && c != ':')
2686 flags |= CD_PRINT;
2687 docd:
2688 if (!docd(p, flags))
2689 goto out;
2690 break;
2691 }
2692 } while (path);
2693 ash_msg_and_raise_error("can't cd to %s", dest);
2694 /* NOTREACHED */
2695 out:
2696 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002697 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002698 return 0;
2699}
2700
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002701static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002702pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002703{
2704 int flags;
2705 const char *dir = curdir;
2706
2707 flags = cdopt();
2708 if (flags) {
2709 if (physdir == nullstr)
2710 setpwd(dir, 0);
2711 dir = physdir;
2712 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002713 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002714 return 0;
2715}
2716
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002717
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002718/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002719
Denis Vlasenko834dee72008-10-07 09:18:30 +00002720
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002721#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002722
Eric Andersenc470f442003-07-28 09:56:35 +00002723/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002724#define CWORD 0 /* character is nothing special */
2725#define CNL 1 /* newline character */
2726#define CBACK 2 /* a backslash character */
2727#define CSQUOTE 3 /* single quote */
2728#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002729#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002730#define CBQUOTE 6 /* backwards single quote */
2731#define CVAR 7 /* a dollar sign */
2732#define CENDVAR 8 /* a '}' character */
2733#define CLP 9 /* a left paren in arithmetic */
2734#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002735#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002736#define CCTL 12 /* like CWORD, except it must be escaped */
2737#define CSPCL 13 /* these terminate a word */
2738#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002739
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002740#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002741#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002742# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002743#endif
2744
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002745#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002746
Mike Frysinger98c52642009-04-02 10:02:37 +00002747#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002748# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002749#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002751#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002752static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002753#if ENABLE_ASH_ALIAS
2754 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2755#endif
2756 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2757 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2758 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2759 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2760 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2761 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2762 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2763 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2764 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2765 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2766 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002767#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002768 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2769 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2770 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2771#endif
2772#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002773};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002774/* Constants below must match table above */
2775enum {
2776#if ENABLE_ASH_ALIAS
2777 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2778#endif
2779 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2780 CNL_CNL_CNL_CNL , /* 2 */
2781 CWORD_CCTL_CCTL_CWORD , /* 3 */
2782 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2783 CVAR_CVAR_CWORD_CVAR , /* 5 */
2784 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2785 CSPCL_CWORD_CWORD_CLP , /* 7 */
2786 CSPCL_CWORD_CWORD_CRP , /* 8 */
2787 CBACK_CBACK_CCTL_CBACK , /* 9 */
2788 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2789 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2790 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2791 CWORD_CWORD_CWORD_CWORD , /* 13 */
2792 CCTL_CCTL_CCTL_CCTL , /* 14 */
2793};
Eric Andersen2870d962001-07-02 17:27:21 +00002794
Denys Vlasenkocd716832009-11-28 22:14:02 +01002795/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2796 * caller must ensure proper cast on it if c is *char_ptr!
2797 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002798/* Values for syntax param */
2799#define BASESYNTAX 0 /* not in quotes */
2800#define DQSYNTAX 1 /* in double quotes */
2801#define SQSYNTAX 2 /* in single quotes */
2802#define ARISYNTAX 3 /* in arithmetic */
2803#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002804
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002805#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002806
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002807static int
2808SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002809{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002810 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002811# if ENABLE_ASH_ALIAS
2812 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002813 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2814 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2815 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2816 11, 3 /* "}~" */
2817 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002818# else
2819 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002820 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2821 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2822 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2823 10, 2 /* "}~" */
2824 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002825# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002826 const char *s;
2827 int indx;
2828
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002829 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002830 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002831# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002832 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002833 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002834 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002835# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002836 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002837 /* Cast is purely for paranoia here,
2838 * just in case someone passed signed char to us */
2839 if ((unsigned char)c >= CTL_FIRST
2840 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002841 ) {
2842 return CCTL;
2843 }
2844 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002845 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002846 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002847 indx = syntax_index_table[s - spec_symbls];
2848 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002849 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002850}
2851
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002852#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002853
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002854static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002855 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002856 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2866 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2867 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2888 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2889 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2890 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2891 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2892 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2893 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2894 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2895 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2896 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2897 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2898 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2899 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2901 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2902 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2903 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2904 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2915 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2916 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2917 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2918 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2919 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2920 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2948 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2949 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2950 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2953 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2980 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2981 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2982 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2983 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2984 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2986 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2987 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2988 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2989 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2990 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2991 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2992 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2993 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3107 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3108 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3109 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3110 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3111 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003112 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003113# if ENABLE_ASH_ALIAS
3114 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3115# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003116};
3117
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003118# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003119
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003120#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003121
Eric Andersen2870d962001-07-02 17:27:21 +00003122
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003123/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003124
Denis Vlasenko131ae172007-02-18 13:00:19 +00003125#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003126
3127#define ALIASINUSE 1
3128#define ALIASDEAD 2
3129
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003130struct alias {
3131 struct alias *next;
3132 char *name;
3133 char *val;
3134 int flag;
3135};
3136
Denis Vlasenko01631112007-12-16 17:20:38 +00003137
3138static struct alias **atab; // [ATABSIZE];
3139#define INIT_G_alias() do { \
3140 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3141} while (0)
3142
Eric Andersen2870d962001-07-02 17:27:21 +00003143
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003144static struct alias **
3145__lookupalias(const char *name) {
3146 unsigned int hashval;
3147 struct alias **app;
3148 const char *p;
3149 unsigned int ch;
3150
3151 p = name;
3152
3153 ch = (unsigned char)*p;
3154 hashval = ch << 4;
3155 while (ch) {
3156 hashval += ch;
3157 ch = (unsigned char)*++p;
3158 }
3159 app = &atab[hashval % ATABSIZE];
3160
3161 for (; *app; app = &(*app)->next) {
3162 if (strcmp(name, (*app)->name) == 0) {
3163 break;
3164 }
3165 }
3166
3167 return app;
3168}
3169
3170static struct alias *
3171lookupalias(const char *name, int check)
3172{
3173 struct alias *ap = *__lookupalias(name);
3174
3175 if (check && ap && (ap->flag & ALIASINUSE))
3176 return NULL;
3177 return ap;
3178}
3179
3180static struct alias *
3181freealias(struct alias *ap)
3182{
3183 struct alias *next;
3184
3185 if (ap->flag & ALIASINUSE) {
3186 ap->flag |= ALIASDEAD;
3187 return ap;
3188 }
3189
3190 next = ap->next;
3191 free(ap->name);
3192 free(ap->val);
3193 free(ap);
3194 return next;
3195}
Eric Andersencb57d552001-06-28 07:25:16 +00003196
Eric Andersenc470f442003-07-28 09:56:35 +00003197static void
3198setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003199{
3200 struct alias *ap, **app;
3201
3202 app = __lookupalias(name);
3203 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003204 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003205 if (ap) {
3206 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003207 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003208 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003209 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003210 ap->flag &= ~ALIASDEAD;
3211 } else {
3212 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003213 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003214 ap->name = ckstrdup(name);
3215 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003216 /*ap->flag = 0; - ckzalloc did it */
3217 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003218 *app = ap;
3219 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003220 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003221}
3222
Eric Andersenc470f442003-07-28 09:56:35 +00003223static int
3224unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003225{
Eric Andersencb57d552001-06-28 07:25:16 +00003226 struct alias **app;
3227
3228 app = __lookupalias(name);
3229
3230 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003231 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003232 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003233 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003234 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003235 }
3236
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003237 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003238}
3239
Eric Andersenc470f442003-07-28 09:56:35 +00003240static void
3241rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003242{
Eric Andersencb57d552001-06-28 07:25:16 +00003243 struct alias *ap, **app;
3244 int i;
3245
Denis Vlasenkob012b102007-02-19 22:43:01 +00003246 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003247 for (i = 0; i < ATABSIZE; i++) {
3248 app = &atab[i];
3249 for (ap = *app; ap; ap = *app) {
3250 *app = freealias(*app);
3251 if (ap == *app) {
3252 app = &ap->next;
3253 }
3254 }
3255 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003256 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003257}
3258
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003259static void
3260printalias(const struct alias *ap)
3261{
3262 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3263}
3264
Eric Andersencb57d552001-06-28 07:25:16 +00003265/*
3266 * TODO - sort output
3267 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003268static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003269aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003270{
3271 char *n, *v;
3272 int ret = 0;
3273 struct alias *ap;
3274
Denis Vlasenko68404f12008-03-17 09:00:54 +00003275 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003276 int i;
3277
Denis Vlasenko68404f12008-03-17 09:00:54 +00003278 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003279 for (ap = atab[i]; ap; ap = ap->next) {
3280 printalias(ap);
3281 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003282 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003283 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003284 }
3285 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003286 v = strchr(n+1, '=');
3287 if (v == NULL) { /* n+1: funny ksh stuff */
3288 ap = *__lookupalias(n);
3289 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003290 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003291 ret = 1;
3292 } else
3293 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003294 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003295 *v++ = '\0';
3296 setalias(n, v);
3297 }
3298 }
3299
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003300 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003301}
3302
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003303static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003304unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003305{
3306 int i;
3307
3308 while ((i = nextopt("a")) != '\0') {
3309 if (i == 'a') {
3310 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003311 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003312 }
3313 }
3314 for (i = 0; *argptr; argptr++) {
3315 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003316 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003317 i = 1;
3318 }
3319 }
3320
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003321 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003322}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003323
Denis Vlasenko131ae172007-02-18 13:00:19 +00003324#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003325
Eric Andersenc470f442003-07-28 09:56:35 +00003326
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003327/* ============ jobs.c */
3328
3329/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003330#define FORK_FG 0
3331#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003332#define FORK_NOJOB 2
3333
3334/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003335#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3336#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3337#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339/*
3340 * A job structure contains information about a job. A job is either a
3341 * single process or a set of processes contained in a pipeline. In the
3342 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3343 * array of pids.
3344 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003345struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003346 pid_t ps_pid; /* process id */
3347 int ps_status; /* last process status from wait() */
3348 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003349};
3350
3351struct job {
3352 struct procstat ps0; /* status of process */
3353 struct procstat *ps; /* status or processes when more than one */
3354#if JOBS
3355 int stopstatus; /* status of a stopped job */
3356#endif
3357 uint32_t
3358 nprocs: 16, /* number of processes */
3359 state: 8,
3360#define JOBRUNNING 0 /* at least one proc running */
3361#define JOBSTOPPED 1 /* all procs are stopped */
3362#define JOBDONE 2 /* all procs are completed */
3363#if JOBS
3364 sigint: 1, /* job was killed by SIGINT */
3365 jobctl: 1, /* job running under job control */
3366#endif
3367 waited: 1, /* true if this entry has been waited for */
3368 used: 1, /* true if this entry is in used */
3369 changed: 1; /* true if status has changed */
3370 struct job *prev_job; /* previous job */
3371};
3372
Denis Vlasenko68404f12008-03-17 09:00:54 +00003373static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003374static int forkshell(struct job *, union node *, int);
3375static int waitforjob(struct job *);
3376
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003377#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003378enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003379#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003380#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003381static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003383#endif
3384
3385/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003386 * Ignore a signal.
3387 */
3388static void
3389ignoresig(int signo)
3390{
3391 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3392 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3393 /* No, need to do it */
3394 signal(signo, SIG_IGN);
3395 }
3396 sigmode[signo - 1] = S_HARD_IGN;
3397}
3398
3399/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003400 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003401 */
3402static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003403signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003404{
3405 gotsig[signo - 1] = 1;
3406
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003407 if (signo == SIGINT && !trap[SIGINT]) {
3408 if (!suppress_int) {
3409 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003410 raise_interrupt(); /* does not return */
3411 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003412 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003413 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003414 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003415 }
3416}
3417
3418/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003419 * Set the signal handler for the specified signal. The routine figures
3420 * out what it should be set to.
3421 */
3422static void
3423setsignal(int signo)
3424{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003425 char *t;
3426 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003427 struct sigaction act;
3428
3429 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003430 new_act = S_DFL;
3431 if (t != NULL) { /* trap for this sig is set */
3432 new_act = S_CATCH;
3433 if (t[0] == '\0') /* trap is "": ignore this sig */
3434 new_act = S_IGN;
3435 }
3436
3437 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003438 switch (signo) {
3439 case SIGINT:
3440 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003441 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003442 break;
3443 case SIGQUIT:
3444#if DEBUG
3445 if (debug)
3446 break;
3447#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003448 /* man bash:
3449 * "In all cases, bash ignores SIGQUIT. Non-builtin
3450 * commands run by bash have signal handlers
3451 * set to the values inherited by the shell
3452 * from its parent". */
3453 new_act = S_IGN;
3454 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003455 case SIGTERM:
3456 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003457 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003458 break;
3459#if JOBS
3460 case SIGTSTP:
3461 case SIGTTOU:
3462 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003463 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003464 break;
3465#endif
3466 }
3467 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003468//TODO: if !rootshell, we reset SIGQUIT to DFL,
3469//whereas we have to restore it to what shell got on entry
3470//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471
3472 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003473 cur_act = *t;
3474 if (cur_act == 0) {
3475 /* current setting is not yet known */
3476 if (sigaction(signo, NULL, &act)) {
3477 /* pretend it worked; maybe we should give a warning,
3478 * but other shells don't. We don't alter sigmode,
3479 * so we retry every time.
3480 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003481 return;
3482 }
3483 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003484 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003485 if (mflag
3486 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3487 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003488 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003489 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003490 }
3491 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003492 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003493 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003494
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003495 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003496 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003497 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003498 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003499 break;
3500 case S_IGN:
3501 act.sa_handler = SIG_IGN;
3502 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003503 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003504
3505 /* flags and mask matter only if !DFL and !IGN, but we do it
3506 * for all cases for more deterministic behavior:
3507 */
3508 act.sa_flags = 0;
3509 sigfillset(&act.sa_mask);
3510
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003511 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003512
3513 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003514}
3515
3516/* mode flags for set_curjob */
3517#define CUR_DELETE 2
3518#define CUR_RUNNING 1
3519#define CUR_STOPPED 0
3520
3521/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003522#define DOWAIT_NONBLOCK WNOHANG
3523#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003524
3525#if JOBS
3526/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003527static int initialpgrp; //references:2
3528static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003529#endif
3530/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003531static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003532/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003533static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003534/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003535static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003536/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003537static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003538
3539static void
3540set_curjob(struct job *jp, unsigned mode)
3541{
3542 struct job *jp1;
3543 struct job **jpp, **curp;
3544
3545 /* first remove from list */
3546 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003547 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003548 jp1 = *jpp;
3549 if (jp1 == jp)
3550 break;
3551 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003552 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003553 *jpp = jp1->prev_job;
3554
3555 /* Then re-insert in correct position */
3556 jpp = curp;
3557 switch (mode) {
3558 default:
3559#if DEBUG
3560 abort();
3561#endif
3562 case CUR_DELETE:
3563 /* job being deleted */
3564 break;
3565 case CUR_RUNNING:
3566 /* newly created job or backgrounded job,
3567 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003568 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003569 jp1 = *jpp;
3570#if JOBS
3571 if (!jp1 || jp1->state != JOBSTOPPED)
3572#endif
3573 break;
3574 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003575 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003576 /* FALLTHROUGH */
3577#if JOBS
3578 case CUR_STOPPED:
3579#endif
3580 /* newly stopped job - becomes curjob */
3581 jp->prev_job = *jpp;
3582 *jpp = jp;
3583 break;
3584 }
3585}
3586
3587#if JOBS || DEBUG
3588static int
3589jobno(const struct job *jp)
3590{
3591 return jp - jobtab + 1;
3592}
3593#endif
3594
3595/*
3596 * Convert a job name to a job structure.
3597 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003598#if !JOBS
3599#define getjob(name, getctl) getjob(name)
3600#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003601static struct job *
3602getjob(const char *name, int getctl)
3603{
3604 struct job *jp;
3605 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003606 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607 unsigned num;
3608 int c;
3609 const char *p;
3610 char *(*match)(const char *, const char *);
3611
3612 jp = curjob;
3613 p = name;
3614 if (!p)
3615 goto currentjob;
3616
3617 if (*p != '%')
3618 goto err;
3619
3620 c = *++p;
3621 if (!c)
3622 goto currentjob;
3623
3624 if (!p[1]) {
3625 if (c == '+' || c == '%') {
3626 currentjob:
3627 err_msg = "No current job";
3628 goto check;
3629 }
3630 if (c == '-') {
3631 if (jp)
3632 jp = jp->prev_job;
3633 err_msg = "No previous job";
3634 check:
3635 if (!jp)
3636 goto err;
3637 goto gotit;
3638 }
3639 }
3640
3641 if (is_number(p)) {
3642 num = atoi(p);
3643 if (num < njobs) {
3644 jp = jobtab + num - 1;
3645 if (jp->used)
3646 goto gotit;
3647 goto err;
3648 }
3649 }
3650
3651 match = prefix;
3652 if (*p == '?') {
3653 match = strstr;
3654 p++;
3655 }
3656
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003657 found = NULL;
3658 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003659 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003660 if (found)
3661 goto err;
3662 found = jp;
3663 err_msg = "%s: ambiguous";
3664 }
3665 jp = jp->prev_job;
3666 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003667 if (!found)
3668 goto err;
3669 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003670
3671 gotit:
3672#if JOBS
3673 err_msg = "job %s not created under job control";
3674 if (getctl && jp->jobctl == 0)
3675 goto err;
3676#endif
3677 return jp;
3678 err:
3679 ash_msg_and_raise_error(err_msg, name);
3680}
3681
3682/*
3683 * Mark a job structure as unused.
3684 */
3685static void
3686freejob(struct job *jp)
3687{
3688 struct procstat *ps;
3689 int i;
3690
3691 INT_OFF;
3692 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003693 if (ps->ps_cmd != nullstr)
3694 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003695 }
3696 if (jp->ps != &jp->ps0)
3697 free(jp->ps);
3698 jp->used = 0;
3699 set_curjob(jp, CUR_DELETE);
3700 INT_ON;
3701}
3702
3703#if JOBS
3704static void
3705xtcsetpgrp(int fd, pid_t pgrp)
3706{
3707 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003708 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003709}
3710
3711/*
3712 * Turn job control on and off.
3713 *
3714 * Note: This code assumes that the third arg to ioctl is a character
3715 * pointer, which is true on Berkeley systems but not System V. Since
3716 * System V doesn't have job control yet, this isn't a problem now.
3717 *
3718 * Called with interrupts off.
3719 */
3720static void
3721setjobctl(int on)
3722{
3723 int fd;
3724 int pgrp;
3725
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003726 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003727 return;
3728 if (on) {
3729 int ofd;
3730 ofd = fd = open(_PATH_TTY, O_RDWR);
3731 if (fd < 0) {
3732 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3733 * That sometimes helps to acquire controlling tty.
3734 * Obviously, a workaround for bugs when someone
3735 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003736 fd = 2;
3737 while (!isatty(fd))
3738 if (--fd < 0)
3739 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003740 }
3741 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003742 if (ofd >= 0)
3743 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003744 if (fd < 0)
3745 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003746 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003747 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003748 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003749 pgrp = tcgetpgrp(fd);
3750 if (pgrp < 0) {
3751 out:
3752 ash_msg("can't access tty; job control turned off");
3753 mflag = on = 0;
3754 goto close;
3755 }
3756 if (pgrp == getpgrp())
3757 break;
3758 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003759 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003760 initialpgrp = pgrp;
3761
3762 setsignal(SIGTSTP);
3763 setsignal(SIGTTOU);
3764 setsignal(SIGTTIN);
3765 pgrp = rootpid;
3766 setpgid(0, pgrp);
3767 xtcsetpgrp(fd, pgrp);
3768 } else {
3769 /* turning job control off */
3770 fd = ttyfd;
3771 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003772 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003773 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003774 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775 setpgid(0, pgrp);
3776 setsignal(SIGTSTP);
3777 setsignal(SIGTTOU);
3778 setsignal(SIGTTIN);
3779 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003780 if (fd >= 0)
3781 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782 fd = -1;
3783 }
3784 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003785 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003786}
3787
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003788static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003789killcmd(int argc, char **argv)
3790{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003791 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003792 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003793 do {
3794 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003795 /*
3796 * "kill %N" - job kill
3797 * Converting to pgrp / pid kill
3798 */
3799 struct job *jp;
3800 char *dst;
3801 int j, n;
3802
3803 jp = getjob(argv[i], 0);
3804 /*
3805 * In jobs started under job control, we signal
3806 * entire process group by kill -PGRP_ID.
3807 * This happens, f.e., in interactive shell.
3808 *
3809 * Otherwise, we signal each child via
3810 * kill PID1 PID2 PID3.
3811 * Testcases:
3812 * sh -c 'sleep 1|sleep 1 & kill %1'
3813 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3814 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3815 */
3816 n = jp->nprocs; /* can't be 0 (I hope) */
3817 if (jp->jobctl)
3818 n = 1;
3819 dst = alloca(n * sizeof(int)*4);
3820 argv[i] = dst;
3821 for (j = 0; j < n; j++) {
3822 struct procstat *ps = &jp->ps[j];
3823 /* Skip non-running and not-stopped members
3824 * (i.e. dead members) of the job
3825 */
3826 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3827 continue;
3828 /*
3829 * kill_main has matching code to expect
3830 * leading space. Needed to not confuse
3831 * negative pids with "kill -SIGNAL_NO" syntax
3832 */
3833 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3834 }
3835 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003836 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003837 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003838 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003839 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840}
3841
3842static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003843showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003845 struct procstat *ps;
3846 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003847
Denys Vlasenko285ad152009-12-04 23:02:27 +01003848 psend = jp->ps + jp->nprocs;
3849 for (ps = jp->ps + 1; ps < psend; ps++)
3850 printf(" | %s", ps->ps_cmd);
3851 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003852 flush_stdout_stderr();
3853}
3854
3855
3856static int
3857restartjob(struct job *jp, int mode)
3858{
3859 struct procstat *ps;
3860 int i;
3861 int status;
3862 pid_t pgid;
3863
3864 INT_OFF;
3865 if (jp->state == JOBDONE)
3866 goto out;
3867 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003868 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003869 if (mode == FORK_FG)
3870 xtcsetpgrp(ttyfd, pgid);
3871 killpg(pgid, SIGCONT);
3872 ps = jp->ps;
3873 i = jp->nprocs;
3874 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003875 if (WIFSTOPPED(ps->ps_status)) {
3876 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003877 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003878 ps++;
3879 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003880 out:
3881 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3882 INT_ON;
3883 return status;
3884}
3885
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003886static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003887fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003888{
3889 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003890 int mode;
3891 int retval;
3892
3893 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3894 nextopt(nullstr);
3895 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003896 do {
3897 jp = getjob(*argv, 1);
3898 if (mode == FORK_BG) {
3899 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003900 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003901 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003902 out1str(jp->ps[0].ps_cmd);
3903 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003904 retval = restartjob(jp, mode);
3905 } while (*argv && *++argv);
3906 return retval;
3907}
3908#endif
3909
3910static int
3911sprint_status(char *s, int status, int sigonly)
3912{
3913 int col;
3914 int st;
3915
3916 col = 0;
3917 if (!WIFEXITED(status)) {
3918#if JOBS
3919 if (WIFSTOPPED(status))
3920 st = WSTOPSIG(status);
3921 else
3922#endif
3923 st = WTERMSIG(status);
3924 if (sigonly) {
3925 if (st == SIGINT || st == SIGPIPE)
3926 goto out;
3927#if JOBS
3928 if (WIFSTOPPED(status))
3929 goto out;
3930#endif
3931 }
3932 st &= 0x7f;
Denys Vlasenko7c6f2462011-02-14 17:17:10 +01003933//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003934 col = fmtstr(s, 32, strsignal(st));
3935 if (WCOREDUMP(status)) {
3936 col += fmtstr(s + col, 16, " (core dumped)");
3937 }
3938 } else if (!sigonly) {
3939 st = WEXITSTATUS(status);
3940 if (st)
3941 col = fmtstr(s, 16, "Done(%d)", st);
3942 else
3943 col = fmtstr(s, 16, "Done");
3944 }
3945 out:
3946 return col;
3947}
3948
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003950dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003951{
3952 int pid;
3953 int status;
3954 struct job *jp;
3955 struct job *thisjob;
3956 int state;
3957
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003958 TRACE(("dowait(0x%x) called\n", wait_flags));
3959
3960 /* Do a wait system call. If job control is compiled in, we accept
3961 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3962 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003963 if (doing_jobctl)
3964 wait_flags |= WUNTRACED;
3965 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003966 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3967 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003968 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003969 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003970
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003971 INT_OFF;
3972 thisjob = NULL;
3973 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003974 struct procstat *ps;
3975 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003976 if (jp->state == JOBDONE)
3977 continue;
3978 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003979 ps = jp->ps;
3980 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003981 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003982 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003983 TRACE(("Job %d: changing status of proc %d "
3984 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003985 jobno(jp), pid, ps->ps_status, status));
3986 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003987 thisjob = jp;
3988 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003989 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003990 state = JOBRUNNING;
3991#if JOBS
3992 if (state == JOBRUNNING)
3993 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003994 if (WIFSTOPPED(ps->ps_status)) {
3995 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003996 state = JOBSTOPPED;
3997 }
3998#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003999 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004000 if (thisjob)
4001 goto gotjob;
4002 }
4003#if JOBS
4004 if (!WIFSTOPPED(status))
4005#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004006 jobless--;
4007 goto out;
4008
4009 gotjob:
4010 if (state != JOBRUNNING) {
4011 thisjob->changed = 1;
4012
4013 if (thisjob->state != state) {
4014 TRACE(("Job %d: changing state from %d to %d\n",
4015 jobno(thisjob), thisjob->state, state));
4016 thisjob->state = state;
4017#if JOBS
4018 if (state == JOBSTOPPED) {
4019 set_curjob(thisjob, CUR_STOPPED);
4020 }
4021#endif
4022 }
4023 }
4024
4025 out:
4026 INT_ON;
4027
4028 if (thisjob && thisjob == job) {
4029 char s[48 + 1];
4030 int len;
4031
4032 len = sprint_status(s, status, 1);
4033 if (len) {
4034 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004035 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004036 out2str(s);
4037 }
4038 }
4039 return pid;
4040}
4041
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004042static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004043blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004044{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004045 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004046 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004047 raise_exception(EXSIG);
4048 return pid;
4049}
4050
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004051#if JOBS
4052static void
4053showjob(FILE *out, struct job *jp, int mode)
4054{
4055 struct procstat *ps;
4056 struct procstat *psend;
4057 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004058 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 char s[80];
4060
4061 ps = jp->ps;
4062
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004063 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004064 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004065 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004066 return;
4067 }
4068
4069 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004070 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071
4072 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004073 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004074 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004075 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004077 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004078 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004079
4080 psend = ps + jp->nprocs;
4081
4082 if (jp->state == JOBRUNNING) {
4083 strcpy(s + col, "Running");
4084 col += sizeof("Running") - 1;
4085 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004086 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087 if (jp->state == JOBSTOPPED)
4088 status = jp->stopstatus;
4089 col += sprint_status(s + col, status, 0);
4090 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004091 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004092
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004093 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4094 * or prints several "PID | <cmdN>" lines,
4095 * depending on SHOW_PIDS bit.
4096 * We do not print status of individual processes
4097 * between PID and <cmdN>. bash does it, but not very well:
4098 * first line shows overall job status, not process status,
4099 * making it impossible to know 1st process status.
4100 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004101 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004102 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004103 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004104 s[0] = '\0';
4105 col = 33;
4106 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004107 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004108 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004109 fprintf(out, "%s%*c%s%s",
4110 s,
4111 33 - col >= 0 ? 33 - col : 0, ' ',
4112 ps == jp->ps ? "" : "| ",
4113 ps->ps_cmd
4114 );
4115 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004116 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004117
4118 jp->changed = 0;
4119
4120 if (jp->state == JOBDONE) {
4121 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4122 freejob(jp);
4123 }
4124}
4125
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004126/*
4127 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4128 * statuses have changed since the last call to showjobs.
4129 */
4130static void
4131showjobs(FILE *out, int mode)
4132{
4133 struct job *jp;
4134
Denys Vlasenko883cea42009-07-11 15:31:59 +02004135 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004136
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004137 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004138 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004139 continue;
4140
4141 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004142 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004143 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004144 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004145 }
4146}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004147
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004148static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004149jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004150{
4151 int mode, m;
4152
4153 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004154 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004155 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004156 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004157 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004158 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004159 }
4160
4161 argv = argptr;
4162 if (*argv) {
4163 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004164 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004165 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004166 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004167 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004168 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004169
4170 return 0;
4171}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172#endif /* JOBS */
4173
Michael Abbott359da5e2009-12-04 23:03:29 +01004174/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004175static int
4176getstatus(struct job *job)
4177{
4178 int status;
4179 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004180 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004181
Michael Abbott359da5e2009-12-04 23:03:29 +01004182 /* Fetch last member's status */
4183 ps = job->ps + job->nprocs - 1;
4184 status = ps->ps_status;
4185 if (pipefail) {
4186 /* "set -o pipefail" mode: use last _nonzero_ status */
4187 while (status == 0 && --ps >= job->ps)
4188 status = ps->ps_status;
4189 }
4190
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004191 retval = WEXITSTATUS(status);
4192 if (!WIFEXITED(status)) {
4193#if JOBS
4194 retval = WSTOPSIG(status);
4195 if (!WIFSTOPPED(status))
4196#endif
4197 {
4198 /* XXX: limits number of signals */
4199 retval = WTERMSIG(status);
4200#if JOBS
4201 if (retval == SIGINT)
4202 job->sigint = 1;
4203#endif
4204 }
4205 retval += 128;
4206 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004207 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004208 jobno(job), job->nprocs, status, retval));
4209 return retval;
4210}
4211
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004212static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004213waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214{
4215 struct job *job;
4216 int retval;
4217 struct job *jp;
4218
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004219 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004220 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004221
4222 nextopt(nullstr);
4223 retval = 0;
4224
4225 argv = argptr;
4226 if (!*argv) {
4227 /* wait for all jobs */
4228 for (;;) {
4229 jp = curjob;
4230 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004231 if (!jp) /* no running procs */
4232 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004233 if (jp->state == JOBRUNNING)
4234 break;
4235 jp->waited = 1;
4236 jp = jp->prev_job;
4237 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004238 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004239 /* man bash:
4240 * "When bash is waiting for an asynchronous command via
4241 * the wait builtin, the reception of a signal for which a trap
4242 * has been set will cause the wait builtin to return immediately
4243 * with an exit status greater than 128, immediately after which
4244 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004245 *
4246 * blocking_wait_with_raise_on_sig raises signal handlers
4247 * if it gets no pid (pid < 0). However,
4248 * if child sends us a signal *and immediately exits*,
4249 * blocking_wait_with_raise_on_sig gets pid > 0
4250 * and does not handle pending_sig. Check this case: */
4251 if (pending_sig)
4252 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 }
4254 }
4255
4256 retval = 127;
4257 do {
4258 if (**argv != '%') {
4259 pid_t pid = number(*argv);
4260 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004261 while (1) {
4262 if (!job)
4263 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004264 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004265 break;
4266 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004267 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004268 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004269 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004270 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004271 /* loop until process terminated or stopped */
4272 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004273 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004274 job->waited = 1;
4275 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004276 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004277 } while (*++argv);
4278
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004279 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004280 return retval;
4281}
4282
4283static struct job *
4284growjobtab(void)
4285{
4286 size_t len;
4287 ptrdiff_t offset;
4288 struct job *jp, *jq;
4289
4290 len = njobs * sizeof(*jp);
4291 jq = jobtab;
4292 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4293
4294 offset = (char *)jp - (char *)jq;
4295 if (offset) {
4296 /* Relocate pointers */
4297 size_t l = len;
4298
4299 jq = (struct job *)((char *)jq + l);
4300 while (l) {
4301 l -= sizeof(*jp);
4302 jq--;
4303#define joff(p) ((struct job *)((char *)(p) + l))
4304#define jmove(p) (p) = (void *)((char *)(p) + offset)
4305 if (joff(jp)->ps == &jq->ps0)
4306 jmove(joff(jp)->ps);
4307 if (joff(jp)->prev_job)
4308 jmove(joff(jp)->prev_job);
4309 }
4310 if (curjob)
4311 jmove(curjob);
4312#undef joff
4313#undef jmove
4314 }
4315
4316 njobs += 4;
4317 jobtab = jp;
4318 jp = (struct job *)((char *)jp + len);
4319 jq = jp + 3;
4320 do {
4321 jq->used = 0;
4322 } while (--jq >= jp);
4323 return jp;
4324}
4325
4326/*
4327 * Return a new job structure.
4328 * Called with interrupts off.
4329 */
4330static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004331makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004332{
4333 int i;
4334 struct job *jp;
4335
4336 for (i = njobs, jp = jobtab; ; jp++) {
4337 if (--i < 0) {
4338 jp = growjobtab();
4339 break;
4340 }
4341 if (jp->used == 0)
4342 break;
4343 if (jp->state != JOBDONE || !jp->waited)
4344 continue;
4345#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004346 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004347 continue;
4348#endif
4349 freejob(jp);
4350 break;
4351 }
4352 memset(jp, 0, sizeof(*jp));
4353#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004354 /* jp->jobctl is a bitfield.
4355 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004356 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004357 jp->jobctl = 1;
4358#endif
4359 jp->prev_job = curjob;
4360 curjob = jp;
4361 jp->used = 1;
4362 jp->ps = &jp->ps0;
4363 if (nprocs > 1) {
4364 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4365 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004366 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004367 jobno(jp)));
4368 return jp;
4369}
4370
4371#if JOBS
4372/*
4373 * Return a string identifying a command (to be printed by the
4374 * jobs command).
4375 */
4376static char *cmdnextc;
4377
4378static void
4379cmdputs(const char *s)
4380{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004381 static const char vstype[VSTYPE + 1][3] = {
4382 "", "}", "-", "+", "?", "=",
4383 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004384 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004385 };
4386
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004388 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004389 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004390 unsigned char c;
4391 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004392 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004393
Denys Vlasenko46a14772009-12-10 21:27:13 +01004394 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004395 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4396 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004397 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004398 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004399 switch (c) {
4400 case CTLESC:
4401 c = *p++;
4402 break;
4403 case CTLVAR:
4404 subtype = *p++;
4405 if ((subtype & VSTYPE) == VSLENGTH)
4406 str = "${#";
4407 else
4408 str = "${";
4409 if (!(subtype & VSQUOTE) == !(quoted & 1))
4410 goto dostr;
4411 quoted ^= 1;
4412 c = '"';
4413 break;
4414 case CTLENDVAR:
4415 str = "\"}" + !(quoted & 1);
4416 quoted >>= 1;
4417 subtype = 0;
4418 goto dostr;
4419 case CTLBACKQ:
4420 str = "$(...)";
4421 goto dostr;
4422 case CTLBACKQ+CTLQUOTE:
4423 str = "\"$(...)\"";
4424 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004425#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004426 case CTLARI:
4427 str = "$((";
4428 goto dostr;
4429 case CTLENDARI:
4430 str = "))";
4431 goto dostr;
4432#endif
4433 case CTLQUOTEMARK:
4434 quoted ^= 1;
4435 c = '"';
4436 break;
4437 case '=':
4438 if (subtype == 0)
4439 break;
4440 if ((subtype & VSTYPE) != VSNORMAL)
4441 quoted <<= 1;
4442 str = vstype[subtype & VSTYPE];
4443 if (subtype & VSNUL)
4444 c = ':';
4445 else
4446 goto checkstr;
4447 break;
4448 case '\'':
4449 case '\\':
4450 case '"':
4451 case '$':
4452 /* These can only happen inside quotes */
4453 cc[0] = c;
4454 str = cc;
4455 c = '\\';
4456 break;
4457 default:
4458 break;
4459 }
4460 USTPUTC(c, nextc);
4461 checkstr:
4462 if (!str)
4463 continue;
4464 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004465 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004466 USTPUTC(c, nextc);
4467 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004468 } /* while *p++ not NUL */
4469
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004470 if (quoted & 1) {
4471 USTPUTC('"', nextc);
4472 }
4473 *nextc = 0;
4474 cmdnextc = nextc;
4475}
4476
4477/* cmdtxt() and cmdlist() call each other */
4478static void cmdtxt(union node *n);
4479
4480static void
4481cmdlist(union node *np, int sep)
4482{
4483 for (; np; np = np->narg.next) {
4484 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004485 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004486 cmdtxt(np);
4487 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004488 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489 }
4490}
4491
4492static void
4493cmdtxt(union node *n)
4494{
4495 union node *np;
4496 struct nodelist *lp;
4497 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004498
4499 if (!n)
4500 return;
4501 switch (n->type) {
4502 default:
4503#if DEBUG
4504 abort();
4505#endif
4506 case NPIPE:
4507 lp = n->npipe.cmdlist;
4508 for (;;) {
4509 cmdtxt(lp->n);
4510 lp = lp->next;
4511 if (!lp)
4512 break;
4513 cmdputs(" | ");
4514 }
4515 break;
4516 case NSEMI:
4517 p = "; ";
4518 goto binop;
4519 case NAND:
4520 p = " && ";
4521 goto binop;
4522 case NOR:
4523 p = " || ";
4524 binop:
4525 cmdtxt(n->nbinary.ch1);
4526 cmdputs(p);
4527 n = n->nbinary.ch2;
4528 goto donode;
4529 case NREDIR:
4530 case NBACKGND:
4531 n = n->nredir.n;
4532 goto donode;
4533 case NNOT:
4534 cmdputs("!");
4535 n = n->nnot.com;
4536 donode:
4537 cmdtxt(n);
4538 break;
4539 case NIF:
4540 cmdputs("if ");
4541 cmdtxt(n->nif.test);
4542 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004543 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004544 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545 cmdputs("; else ");
4546 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004547 } else {
4548 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004549 }
4550 p = "; fi";
4551 goto dotail;
4552 case NSUBSHELL:
4553 cmdputs("(");
4554 n = n->nredir.n;
4555 p = ")";
4556 goto dotail;
4557 case NWHILE:
4558 p = "while ";
4559 goto until;
4560 case NUNTIL:
4561 p = "until ";
4562 until:
4563 cmdputs(p);
4564 cmdtxt(n->nbinary.ch1);
4565 n = n->nbinary.ch2;
4566 p = "; done";
4567 dodo:
4568 cmdputs("; do ");
4569 dotail:
4570 cmdtxt(n);
4571 goto dotail2;
4572 case NFOR:
4573 cmdputs("for ");
4574 cmdputs(n->nfor.var);
4575 cmdputs(" in ");
4576 cmdlist(n->nfor.args, 1);
4577 n = n->nfor.body;
4578 p = "; done";
4579 goto dodo;
4580 case NDEFUN:
4581 cmdputs(n->narg.text);
4582 p = "() { ... }";
4583 goto dotail2;
4584 case NCMD:
4585 cmdlist(n->ncmd.args, 1);
4586 cmdlist(n->ncmd.redirect, 0);
4587 break;
4588 case NARG:
4589 p = n->narg.text;
4590 dotail2:
4591 cmdputs(p);
4592 break;
4593 case NHERE:
4594 case NXHERE:
4595 p = "<<...";
4596 goto dotail2;
4597 case NCASE:
4598 cmdputs("case ");
4599 cmdputs(n->ncase.expr->narg.text);
4600 cmdputs(" in ");
4601 for (np = n->ncase.cases; np; np = np->nclist.next) {
4602 cmdtxt(np->nclist.pattern);
4603 cmdputs(") ");
4604 cmdtxt(np->nclist.body);
4605 cmdputs(";; ");
4606 }
4607 p = "esac";
4608 goto dotail2;
4609 case NTO:
4610 p = ">";
4611 goto redir;
4612 case NCLOBBER:
4613 p = ">|";
4614 goto redir;
4615 case NAPPEND:
4616 p = ">>";
4617 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004618#if ENABLE_ASH_BASH_COMPAT
4619 case NTO2:
4620#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004621 case NTOFD:
4622 p = ">&";
4623 goto redir;
4624 case NFROM:
4625 p = "<";
4626 goto redir;
4627 case NFROMFD:
4628 p = "<&";
4629 goto redir;
4630 case NFROMTO:
4631 p = "<>";
4632 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004633 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004634 cmdputs(p);
4635 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004636 cmdputs(utoa(n->ndup.dupfd));
4637 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004638 }
4639 n = n->nfile.fname;
4640 goto donode;
4641 }
4642}
4643
4644static char *
4645commandtext(union node *n)
4646{
4647 char *name;
4648
4649 STARTSTACKSTR(cmdnextc);
4650 cmdtxt(n);
4651 name = stackblock();
4652 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4653 name, cmdnextc, cmdnextc));
4654 return ckstrdup(name);
4655}
4656#endif /* JOBS */
4657
4658/*
4659 * Fork off a subshell. If we are doing job control, give the subshell its
4660 * own process group. Jp is a job structure that the job is to be added to.
4661 * N is the command that will be evaluated by the child. Both jp and n may
4662 * be NULL. The mode parameter can be one of the following:
4663 * FORK_FG - Fork off a foreground process.
4664 * FORK_BG - Fork off a background process.
4665 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4666 * process group even if job control is on.
4667 *
4668 * When job control is turned off, background processes have their standard
4669 * input redirected to /dev/null (except for the second and later processes
4670 * in a pipeline).
4671 *
4672 * Called with interrupts off.
4673 */
4674/*
4675 * Clear traps on a fork.
4676 */
4677static void
4678clear_traps(void)
4679{
4680 char **tp;
4681
4682 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004683 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004684 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004685 if (trap_ptr == trap)
4686 free(*tp);
4687 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004688 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004689 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004690 setsignal(tp - trap);
4691 INT_ON;
4692 }
4693 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004694 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004695}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004696
4697/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004698static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004699
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004700/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004701static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004702forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004703{
4704 int oldlvl;
4705
4706 TRACE(("Child shell %d\n", getpid()));
4707 oldlvl = shlvl;
4708 shlvl++;
4709
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004710 /* man bash: "Non-builtin commands run by bash have signal handlers
4711 * set to the values inherited by the shell from its parent".
4712 * Do we do it correctly? */
4713
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004714 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004715
4716 if (mode == FORK_NOJOB /* is it `xxx` ? */
4717 && n && n->type == NCMD /* is it single cmd? */
4718 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004719 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004720 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4721 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4722 ) {
4723 TRACE(("Trap hack\n"));
4724 /* Awful hack for `trap` or $(trap).
4725 *
4726 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4727 * contains an example where "trap" is executed in a subshell:
4728 *
4729 * save_traps=$(trap)
4730 * ...
4731 * eval "$save_traps"
4732 *
4733 * Standard does not say that "trap" in subshell shall print
4734 * parent shell's traps. It only says that its output
4735 * must have suitable form, but then, in the above example
4736 * (which is not supposed to be normative), it implies that.
4737 *
4738 * bash (and probably other shell) does implement it
4739 * (traps are reset to defaults, but "trap" still shows them),
4740 * but as a result, "trap" logic is hopelessly messed up:
4741 *
4742 * # trap
4743 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4744 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4745 * # true | trap <--- trap is in subshell - no output (ditto)
4746 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4747 * trap -- 'echo Ho' SIGWINCH
4748 * # echo `(trap)` <--- in subshell in subshell - output
4749 * trap -- 'echo Ho' SIGWINCH
4750 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4751 * trap -- 'echo Ho' SIGWINCH
4752 *
4753 * The rules when to forget and when to not forget traps
4754 * get really complex and nonsensical.
4755 *
4756 * Our solution: ONLY bare $(trap) or `trap` is special.
4757 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004758 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004759 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004760 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004761 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004762 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004763#if JOBS
4764 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004765 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004766 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004767 pid_t pgrp;
4768
4769 if (jp->nprocs == 0)
4770 pgrp = getpid();
4771 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004772 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004773 /* this can fail because we are doing it in the parent also */
4774 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004775 if (mode == FORK_FG)
4776 xtcsetpgrp(ttyfd, pgrp);
4777 setsignal(SIGTSTP);
4778 setsignal(SIGTTOU);
4779 } else
4780#endif
4781 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004782 /* man bash: "When job control is not in effect,
4783 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004784 ignoresig(SIGINT);
4785 ignoresig(SIGQUIT);
4786 if (jp->nprocs == 0) {
4787 close(0);
4788 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004789 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004790 }
4791 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004792 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004793 if (iflag) { /* why if iflag only? */
4794 setsignal(SIGINT);
4795 setsignal(SIGTERM);
4796 }
4797 /* man bash:
4798 * "In all cases, bash ignores SIGQUIT. Non-builtin
4799 * commands run by bash have signal handlers
4800 * set to the values inherited by the shell
4801 * from its parent".
4802 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004803 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004804 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004805#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004806 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004807 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004808 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004809 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004810 /* "jobs": we do not want to clear job list for it,
4811 * instead we remove only _its_ own_ job from job list.
4812 * This makes "jobs .... | cat" more useful.
4813 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004814 freejob(curjob);
4815 return;
4816 }
4817#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004818 for (jp = curjob; jp; jp = jp->prev_job)
4819 freejob(jp);
4820 jobless = 0;
4821}
4822
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004823/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004824#if !JOBS
4825#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4826#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004827static void
4828forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4829{
4830 TRACE(("In parent shell: child = %d\n", pid));
4831 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004832 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4833 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004834 jobless++;
4835 return;
4836 }
4837#if JOBS
4838 if (mode != FORK_NOJOB && jp->jobctl) {
4839 int pgrp;
4840
4841 if (jp->nprocs == 0)
4842 pgrp = pid;
4843 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004844 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004845 /* This can fail because we are doing it in the child also */
4846 setpgid(pid, pgrp);
4847 }
4848#endif
4849 if (mode == FORK_BG) {
4850 backgndpid = pid; /* set $! */
4851 set_curjob(jp, CUR_RUNNING);
4852 }
4853 if (jp) {
4854 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004855 ps->ps_pid = pid;
4856 ps->ps_status = -1;
4857 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004858#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004859 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004860 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004861#endif
4862 }
4863}
4864
4865static int
4866forkshell(struct job *jp, union node *n, int mode)
4867{
4868 int pid;
4869
4870 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4871 pid = fork();
4872 if (pid < 0) {
4873 TRACE(("Fork failed, errno=%d", errno));
4874 if (jp)
4875 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004876 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004877 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004878 if (pid == 0) {
4879 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004880 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004881 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004882 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004883 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004884 return pid;
4885}
4886
4887/*
4888 * Wait for job to finish.
4889 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004890 * Under job control we have the problem that while a child process
4891 * is running interrupts generated by the user are sent to the child
4892 * but not to the shell. This means that an infinite loop started by
4893 * an interactive user may be hard to kill. With job control turned off,
4894 * an interactive user may place an interactive program inside a loop.
4895 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004896 * these interrupts to also abort the loop. The approach we take here
4897 * is to have the shell ignore interrupt signals while waiting for a
4898 * foreground process to terminate, and then send itself an interrupt
4899 * signal if the child process was terminated by an interrupt signal.
4900 * Unfortunately, some programs want to do a bit of cleanup and then
4901 * exit on interrupt; unless these processes terminate themselves by
4902 * sending a signal to themselves (instead of calling exit) they will
4903 * confuse this approach.
4904 *
4905 * Called with interrupts off.
4906 */
4907static int
4908waitforjob(struct job *jp)
4909{
4910 int st;
4911
4912 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004913
4914 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004915 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004916 /* In non-interactive shells, we _can_ get
4917 * a keyboard signal here and be EINTRed,
4918 * but we just loop back, waiting for command to complete.
4919 *
4920 * man bash:
4921 * "If bash is waiting for a command to complete and receives
4922 * a signal for which a trap has been set, the trap
4923 * will not be executed until the command completes."
4924 *
4925 * Reality is that even if trap is not set, bash
4926 * will not act on the signal until command completes.
4927 * Try this. sleep5intoff.c:
4928 * #include <signal.h>
4929 * #include <unistd.h>
4930 * int main() {
4931 * sigset_t set;
4932 * sigemptyset(&set);
4933 * sigaddset(&set, SIGINT);
4934 * sigaddset(&set, SIGQUIT);
4935 * sigprocmask(SIG_BLOCK, &set, NULL);
4936 * sleep(5);
4937 * return 0;
4938 * }
4939 * $ bash -c './sleep5intoff; echo hi'
4940 * ^C^C^C^C <--- pressing ^C once a second
4941 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004942 * $ bash -c './sleep5intoff; echo hi'
4943 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4944 * $ _
4945 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004946 dowait(DOWAIT_BLOCK, jp);
4947 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004948 INT_ON;
4949
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004950 st = getstatus(jp);
4951#if JOBS
4952 if (jp->jobctl) {
4953 xtcsetpgrp(ttyfd, rootpid);
4954 /*
4955 * This is truly gross.
4956 * If we're doing job control, then we did a TIOCSPGRP which
4957 * caused us (the shell) to no longer be in the controlling
4958 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4959 * intuit from the subprocess exit status whether a SIGINT
4960 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4961 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004962 if (jp->sigint) /* TODO: do the same with all signals */
4963 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004964 }
4965 if (jp->state == JOBDONE)
4966#endif
4967 freejob(jp);
4968 return st;
4969}
4970
4971/*
4972 * return 1 if there are stopped jobs, otherwise 0
4973 */
4974static int
4975stoppedjobs(void)
4976{
4977 struct job *jp;
4978 int retval;
4979
4980 retval = 0;
4981 if (job_warning)
4982 goto out;
4983 jp = curjob;
4984 if (jp && jp->state == JOBSTOPPED) {
4985 out2str("You have stopped jobs.\n");
4986 job_warning = 2;
4987 retval++;
4988 }
4989 out:
4990 return retval;
4991}
4992
4993
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004994/* ============ redir.c
4995 *
4996 * Code for dealing with input/output redirection.
4997 */
4998
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004999#undef EMPTY
5000#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005001#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005002#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005003
5004/*
5005 * Open a file in noclobber mode.
5006 * The code was copied from bash.
5007 */
5008static int
5009noclobberopen(const char *fname)
5010{
5011 int r, fd;
5012 struct stat finfo, finfo2;
5013
5014 /*
5015 * If the file exists and is a regular file, return an error
5016 * immediately.
5017 */
5018 r = stat(fname, &finfo);
5019 if (r == 0 && S_ISREG(finfo.st_mode)) {
5020 errno = EEXIST;
5021 return -1;
5022 }
5023
5024 /*
5025 * If the file was not present (r != 0), make sure we open it
5026 * exclusively so that if it is created before we open it, our open
5027 * will fail. Make sure that we do not truncate an existing file.
5028 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5029 * file was not a regular file, we leave O_EXCL off.
5030 */
5031 if (r != 0)
5032 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5033 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5034
5035 /* If the open failed, return the file descriptor right away. */
5036 if (fd < 0)
5037 return fd;
5038
5039 /*
5040 * OK, the open succeeded, but the file may have been changed from a
5041 * non-regular file to a regular file between the stat and the open.
5042 * We are assuming that the O_EXCL open handles the case where FILENAME
5043 * did not exist and is symlinked to an existing file between the stat
5044 * and open.
5045 */
5046
5047 /*
5048 * If we can open it and fstat the file descriptor, and neither check
5049 * revealed that it was a regular file, and the file has not been
5050 * replaced, return the file descriptor.
5051 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005052 if (fstat(fd, &finfo2) == 0
5053 && !S_ISREG(finfo2.st_mode)
5054 && finfo.st_dev == finfo2.st_dev
5055 && finfo.st_ino == finfo2.st_ino
5056 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005057 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005058 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005059
5060 /* The file has been replaced. badness. */
5061 close(fd);
5062 errno = EEXIST;
5063 return -1;
5064}
5065
5066/*
5067 * Handle here documents. Normally we fork off a process to write the
5068 * data to a pipe. If the document is short, we can stuff the data in
5069 * the pipe without forking.
5070 */
5071/* openhere needs this forward reference */
5072static void expandhere(union node *arg, int fd);
5073static int
5074openhere(union node *redir)
5075{
5076 int pip[2];
5077 size_t len = 0;
5078
5079 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005080 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005081 if (redir->type == NHERE) {
5082 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005083 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005084 full_write(pip[1], redir->nhere.doc->narg.text, len);
5085 goto out;
5086 }
5087 }
5088 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005089 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005090 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005091 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5092 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5093 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5094 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005095 signal(SIGPIPE, SIG_DFL);
5096 if (redir->type == NHERE)
5097 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005098 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005099 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005100 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005101 }
5102 out:
5103 close(pip[1]);
5104 return pip[0];
5105}
5106
5107static int
5108openredirect(union node *redir)
5109{
5110 char *fname;
5111 int f;
5112
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02005113 fname = redir->nfile.expfname;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005114 switch (redir->nfile.type) {
5115 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005116 f = open(fname, O_RDONLY);
5117 if (f < 0)
5118 goto eopen;
5119 break;
5120 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005121 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122 if (f < 0)
5123 goto ecreate;
5124 break;
5125 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005126#if ENABLE_ASH_BASH_COMPAT
5127 case NTO2:
5128#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005129 /* Take care of noclobber mode. */
5130 if (Cflag) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005131 f = noclobberopen(fname);
5132 if (f < 0)
5133 goto ecreate;
5134 break;
5135 }
5136 /* FALLTHROUGH */
5137 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005138 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5139 if (f < 0)
5140 goto ecreate;
5141 break;
5142 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005143 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5144 if (f < 0)
5145 goto ecreate;
5146 break;
5147 default:
5148#if DEBUG
5149 abort();
5150#endif
5151 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005152/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005153// case NTOFD:
5154// case NFROMFD:
5155// f = -1;
5156// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005157 case NHERE:
5158 case NXHERE:
5159 f = openhere(redir);
5160 break;
5161 }
5162
5163 return f;
5164 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005165 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005166 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005167 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005168}
5169
5170/*
5171 * Copy a file descriptor to be >= to. Returns -1
5172 * if the source file descriptor is closed, EMPTY if there are no unused
5173 * file descriptors left.
5174 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005175/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5176 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005177enum {
5178 COPYFD_EXACT = (int)~(INT_MAX),
5179 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5180};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005181static int
5182copyfd(int from, int to)
5183{
5184 int newfd;
5185
Denis Vlasenko5a867312008-07-24 19:46:38 +00005186 if (to & COPYFD_EXACT) {
5187 to &= ~COPYFD_EXACT;
5188 /*if (from != to)*/
5189 newfd = dup2(from, to);
5190 } else {
5191 newfd = fcntl(from, F_DUPFD, to);
5192 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005193 if (newfd < 0) {
5194 if (errno == EMFILE)
5195 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005196 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005197 ash_msg_and_raise_error("%d: %m", from);
5198 }
5199 return newfd;
5200}
5201
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005202/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005203struct two_fd_t {
5204 int orig, copy;
5205};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005206struct redirtab {
5207 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005208 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005209 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005210 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005211};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005212#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005213
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005214static int need_to_remember(struct redirtab *rp, int fd)
5215{
5216 int i;
5217
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005218 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005219 return 0;
5220
5221 for (i = 0; i < rp->pair_count; i++) {
5222 if (rp->two_fd[i].orig == fd) {
5223 /* already remembered */
5224 return 0;
5225 }
5226 }
5227 return 1;
5228}
5229
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005230/* "hidden" fd is a fd used to read scripts, or a copy of such */
5231static int is_hidden_fd(struct redirtab *rp, int fd)
5232{
5233 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005234 struct parsefile *pf;
5235
5236 if (fd == -1)
5237 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005238 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005239 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005240 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005241 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005242 * $ ash # running ash interactively
5243 * $ . ./script.sh
5244 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005245 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005246 * it's still ok to use it: "read" builtin uses it,
5247 * why should we cripple "exec" builtin?
5248 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005249 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005250 return 1;
5251 }
5252 pf = pf->prev;
5253 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005254
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005255 if (!rp)
5256 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005257 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005258 fd |= COPYFD_RESTORE;
5259 for (i = 0; i < rp->pair_count; i++) {
5260 if (rp->two_fd[i].copy == fd) {
5261 return 1;
5262 }
5263 }
5264 return 0;
5265}
5266
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005267/*
5268 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5269 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005270 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005271 */
5272/* flags passed to redirect */
5273#define REDIR_PUSH 01 /* save previous values of file descriptors */
5274#define REDIR_SAVEFD2 03 /* set preverrout */
5275static void
5276redirect(union node *redir, int flags)
5277{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005278 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005279 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005280 int i;
5281 int fd;
5282 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005283 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005284
Denis Vlasenko01631112007-12-16 17:20:38 +00005285 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005286 if (!redir) {
5287 return;
5288 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005289
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005290 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005291 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005292 INT_OFF;
5293 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005294 union node *tmp = redir;
5295 do {
5296 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005297#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005298 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005299 sv_pos++;
5300#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005301 tmp = tmp->nfile.next;
5302 } while (tmp);
5303 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005304 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005305 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005306 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005307 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005308 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005309 while (sv_pos > 0) {
5310 sv_pos--;
5311 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5312 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005313 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005314
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005315 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005316 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005317 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005318 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005319 right_fd = redir->ndup.dupfd;
5320 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005321 /* redirect from/to same file descriptor? */
5322 if (right_fd == fd)
5323 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005324 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005325 if (is_hidden_fd(sv, right_fd)) {
5326 errno = EBADF; /* as if it is closed */
5327 ash_msg_and_raise_error("%d: %m", right_fd);
5328 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005329 newfd = -1;
5330 } else {
5331 newfd = openredirect(redir); /* always >= 0 */
5332 if (fd == newfd) {
5333 /* Descriptor wasn't open before redirect.
5334 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005335 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005336 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005337 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005338 continue;
5339 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005340 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005341#if ENABLE_ASH_BASH_COMPAT
5342 redirect_more:
5343#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005344 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005345 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005346 /* Careful to not accidentally "save"
5347 * to the same fd as right side fd in N>&M */
5348 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5349 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005350/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5351 * are closed in popredir() in the child, preventing them from leaking
5352 * into child. (popredir() also cleans up the mess in case of failures)
5353 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005354 if (i == -1) {
5355 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005356 if (i != EBADF) {
5357 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005358 if (newfd >= 0)
5359 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005360 errno = i;
5361 ash_msg_and_raise_error("%d: %m", fd);
5362 /* NOTREACHED */
5363 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005364 /* EBADF: it is not open - good, remember to close it */
5365 remember_to_close:
5366 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005367 } else { /* fd is open, save its copy */
5368 /* "exec fd>&-" should not close fds
5369 * which point to script file(s).
5370 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005371 if (is_hidden_fd(sv, fd))
5372 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005373 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005374 if (fd == 2)
5375 copied_fd2 = i;
5376 sv->two_fd[sv_pos].orig = fd;
5377 sv->two_fd[sv_pos].copy = i;
5378 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005379 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005380 if (newfd < 0) {
5381 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005382 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005383 /* Don't want to trigger debugging */
5384 if (fd != -1)
5385 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005386 } else {
5387 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005388 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005389 } else if (fd != newfd) { /* move newfd to fd */
5390 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005391#if ENABLE_ASH_BASH_COMPAT
5392 if (!(redir->nfile.type == NTO2 && fd == 2))
5393#endif
5394 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005395 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005396#if ENABLE_ASH_BASH_COMPAT
5397 if (redir->nfile.type == NTO2 && fd == 1) {
5398 /* We already redirected it to fd 1, now copy it to 2 */
5399 newfd = 1;
5400 fd = 2;
5401 goto redirect_more;
5402 }
5403#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005404 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005405
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005406 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005407 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5408 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005409}
5410
5411/*
5412 * Undo the effects of the last redirection.
5413 */
5414static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005415popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005416{
5417 struct redirtab *rp;
5418 int i;
5419
Denis Vlasenko01631112007-12-16 17:20:38 +00005420 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005421 return;
5422 INT_OFF;
5423 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005424 for (i = 0; i < rp->pair_count; i++) {
5425 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005426 int copy = rp->two_fd[i].copy;
5427 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005428 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005429 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005430 continue;
5431 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005432 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005433 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005434 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005435 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005436 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005437 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005438 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005439 }
5440 }
5441 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005442 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005443 free(rp);
5444 INT_ON;
5445}
5446
5447/*
5448 * Undo all redirections. Called on error or interrupt.
5449 */
5450
5451/*
5452 * Discard all saved file descriptors.
5453 */
5454static void
5455clearredir(int drop)
5456{
5457 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005458 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005459 if (!redirlist)
5460 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005461 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005462 }
5463}
5464
5465static int
5466redirectsafe(union node *redir, int flags)
5467{
5468 int err;
5469 volatile int saveint;
5470 struct jmploc *volatile savehandler = exception_handler;
5471 struct jmploc jmploc;
5472
5473 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005474 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5475 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005476 if (!err) {
5477 exception_handler = &jmploc;
5478 redirect(redir, flags);
5479 }
5480 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005481 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005482 longjmp(exception_handler->loc, 1);
5483 RESTORE_INT(saveint);
5484 return err;
5485}
5486
5487
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005488/* ============ Routines to expand arguments to commands
5489 *
5490 * We have to deal with backquotes, shell variables, and file metacharacters.
5491 */
5492
Mike Frysinger98c52642009-04-02 10:02:37 +00005493#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005494static arith_t
5495ash_arith(const char *s)
5496{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005497 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005498 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005499
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005500 math_state.lookupvar = lookupvar;
5501 math_state.setvar = setvar2;
5502 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005503
5504 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005505 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005506 if (math_state.errmsg)
5507 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005508 INT_ON;
5509
5510 return result;
5511}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005512#endif
5513
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005514/*
5515 * expandarg flags
5516 */
5517#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5518#define EXP_TILDE 0x2 /* do normal tilde expansion */
5519#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5520#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5521#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5522#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5523#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5524#define EXP_WORD 0x80 /* expand word in parameter expansion */
5525#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5526/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005527 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005528 */
5529#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5530#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5531#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5532#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5533#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5534
5535/*
5536 * Structure specifying which parts of the string should be searched
5537 * for IFS characters.
5538 */
5539struct ifsregion {
5540 struct ifsregion *next; /* next region in list */
5541 int begoff; /* offset of start of region */
5542 int endoff; /* offset of end of region */
5543 int nulonly; /* search for nul bytes only */
5544};
5545
5546struct arglist {
5547 struct strlist *list;
5548 struct strlist **lastp;
5549};
5550
5551/* output of current string */
5552static char *expdest;
5553/* list of back quote expressions */
5554static struct nodelist *argbackq;
5555/* first struct in list of ifs regions */
5556static struct ifsregion ifsfirst;
5557/* last struct in list */
5558static struct ifsregion *ifslastp;
5559/* holds expanded arg list */
5560static struct arglist exparg;
5561
5562/*
5563 * Our own itoa().
5564 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005565#if !ENABLE_SH_MATH_SUPPORT
5566/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5567typedef long arith_t;
5568# define ARITH_FMT "%ld"
5569#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005570static int
5571cvtnum(arith_t num)
5572{
5573 int len;
5574
5575 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005576 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005577 STADJUST(len, expdest);
5578 return len;
5579}
5580
5581static size_t
5582esclen(const char *start, const char *p)
5583{
5584 size_t esc = 0;
5585
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005586 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005587 esc++;
5588 }
5589 return esc;
5590}
5591
5592/*
5593 * Remove any CTLESC characters from a string.
5594 */
5595static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005596rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005597{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005598 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005599
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005601 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005602 unsigned protect_against_glob;
5603 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005604
5605 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005606 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005607 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005608
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005609 q = p;
5610 r = str;
5611 if (flag & RMESCAPE_ALLOC) {
5612 size_t len = p - str;
5613 size_t fulllen = len + strlen(p) + 1;
5614
5615 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005616 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005617 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005618 /* p and str may be invalidated by makestrspace */
5619 str = (char *)stackblock() + strloc;
5620 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005621 } else if (flag & RMESCAPE_HEAP) {
5622 r = ckmalloc(fulllen);
5623 } else {
5624 r = stalloc(fulllen);
5625 }
5626 q = r;
5627 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005628 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005629 }
5630 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005631
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005632 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5633 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005634 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005636 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005637// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5638// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5639// Note: both inquotes and protect_against_glob only affect whether
5640// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005641 inquotes = ~inquotes;
5642 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005643 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644 continue;
5645 }
5646 if (*p == '\\') {
5647 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005648 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005649 goto copy;
5650 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005651 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005652 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005653 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005654 *q++ = '\\';
5655 }
5656 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005657 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005658 copy:
5659 *q++ = *p++;
5660 }
5661 *q = '\0';
5662 if (flag & RMESCAPE_GROW) {
5663 expdest = r;
5664 STADJUST(q - r + 1, expdest);
5665 }
5666 return r;
5667}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005668#define pmatch(a, b) !fnmatch((a), (b), 0)
5669
5670/*
5671 * Prepare a pattern for a expmeta (internal glob(3)) call.
5672 *
5673 * Returns an stalloced string.
5674 */
5675static char *
5676preglob(const char *pattern, int quoted, int flag)
5677{
5678 flag |= RMESCAPE_GLOB;
5679 if (quoted) {
5680 flag |= RMESCAPE_QUOTED;
5681 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005682 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005683}
5684
5685/*
5686 * Put a string on the stack.
5687 */
5688static void
5689memtodest(const char *p, size_t len, int syntax, int quotes)
5690{
5691 char *q = expdest;
5692
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005693 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005694
5695 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005696 unsigned char c = *p++;
5697 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005698 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005699 if (quotes) {
5700 int n = SIT(c, syntax);
5701 if (n == CCTL || n == CBACK)
5702 USTPUTC(CTLESC, q);
5703 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005704 USTPUTC(c, q);
5705 }
5706
5707 expdest = q;
5708}
5709
5710static void
5711strtodest(const char *p, int syntax, int quotes)
5712{
5713 memtodest(p, strlen(p), syntax, quotes);
5714}
5715
5716/*
5717 * Record the fact that we have to scan this region of the
5718 * string for IFS characters.
5719 */
5720static void
5721recordregion(int start, int end, int nulonly)
5722{
5723 struct ifsregion *ifsp;
5724
5725 if (ifslastp == NULL) {
5726 ifsp = &ifsfirst;
5727 } else {
5728 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005729 ifsp = ckzalloc(sizeof(*ifsp));
5730 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005731 ifslastp->next = ifsp;
5732 INT_ON;
5733 }
5734 ifslastp = ifsp;
5735 ifslastp->begoff = start;
5736 ifslastp->endoff = end;
5737 ifslastp->nulonly = nulonly;
5738}
5739
5740static void
5741removerecordregions(int endoff)
5742{
5743 if (ifslastp == NULL)
5744 return;
5745
5746 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005747 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005748 struct ifsregion *ifsp;
5749 INT_OFF;
5750 ifsp = ifsfirst.next->next;
5751 free(ifsfirst.next);
5752 ifsfirst.next = ifsp;
5753 INT_ON;
5754 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005755 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005756 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005757 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005758 ifslastp = &ifsfirst;
5759 ifsfirst.endoff = endoff;
5760 }
5761 return;
5762 }
5763
5764 ifslastp = &ifsfirst;
5765 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005766 ifslastp = ifslastp->next;
5767 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005768 struct ifsregion *ifsp;
5769 INT_OFF;
5770 ifsp = ifslastp->next->next;
5771 free(ifslastp->next);
5772 ifslastp->next = ifsp;
5773 INT_ON;
5774 }
5775 if (ifslastp->endoff > endoff)
5776 ifslastp->endoff = endoff;
5777}
5778
5779static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005780exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005781{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005782 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005783 char *name;
5784 struct passwd *pw;
5785 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005786 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005787 int startloc;
5788
5789 name = p + 1;
5790
5791 while ((c = *++p) != '\0') {
5792 switch (c) {
5793 case CTLESC:
5794 return startp;
5795 case CTLQUOTEMARK:
5796 return startp;
5797 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005798 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799 goto done;
5800 break;
5801 case '/':
5802 case CTLENDVAR:
5803 goto done;
5804 }
5805 }
5806 done:
5807 *p = '\0';
5808 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005809 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810 } else {
5811 pw = getpwnam(name);
5812 if (pw == NULL)
5813 goto lose;
5814 home = pw->pw_dir;
5815 }
5816 if (!home || !*home)
5817 goto lose;
5818 *p = c;
5819 startloc = expdest - (char *)stackblock();
5820 strtodest(home, SQSYNTAX, quotes);
5821 recordregion(startloc, expdest - (char *)stackblock(), 0);
5822 return p;
5823 lose:
5824 *p = c;
5825 return startp;
5826}
5827
5828/*
5829 * Execute a command inside back quotes. If it's a builtin command, we
5830 * want to save its output in a block obtained from malloc. Otherwise
5831 * we fork off a subprocess and get the output of the command via a pipe.
5832 * Should be called with interrupts off.
5833 */
5834struct backcmd { /* result of evalbackcmd */
5835 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005836 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005837 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005838 struct job *jp; /* job structure for command */
5839};
5840
5841/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005842static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005843#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005844static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005845
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005846static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005847evalbackcmd(union node *n, struct backcmd *result)
5848{
5849 int saveherefd;
5850
5851 result->fd = -1;
5852 result->buf = NULL;
5853 result->nleft = 0;
5854 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005855 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005856 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005857
5858 saveherefd = herefd;
5859 herefd = -1;
5860
5861 {
5862 int pip[2];
5863 struct job *jp;
5864
5865 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005866 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005867 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005868 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5869 FORCE_INT_ON;
5870 close(pip[0]);
5871 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005872 /*close(1);*/
5873 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005874 close(pip[1]);
5875 }
5876 eflag = 0;
5877 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5878 /* NOTREACHED */
5879 }
5880 close(pip[1]);
5881 result->fd = pip[0];
5882 result->jp = jp;
5883 }
5884 herefd = saveherefd;
5885 out:
5886 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5887 result->fd, result->buf, result->nleft, result->jp));
5888}
5889
5890/*
5891 * Expand stuff in backwards quotes.
5892 */
5893static void
5894expbackq(union node *cmd, int quoted, int quotes)
5895{
5896 struct backcmd in;
5897 int i;
5898 char buf[128];
5899 char *p;
5900 char *dest;
5901 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005902 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005903 struct stackmark smark;
5904
5905 INT_OFF;
5906 setstackmark(&smark);
5907 dest = expdest;
5908 startloc = dest - (char *)stackblock();
5909 grabstackstr(dest);
5910 evalbackcmd(cmd, &in);
5911 popstackmark(&smark);
5912
5913 p = in.buf;
5914 i = in.nleft;
5915 if (i == 0)
5916 goto read;
5917 for (;;) {
5918 memtodest(p, i, syntax, quotes);
5919 read:
5920 if (in.fd < 0)
5921 break;
Denys Vlasenko80542ba2011-05-08 21:23:43 +02005922 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005923 TRACE(("expbackq: read returns %d\n", i));
5924 if (i <= 0)
5925 break;
5926 p = buf;
5927 }
5928
Denis Vlasenko60818682007-09-28 22:07:23 +00005929 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005930 if (in.fd >= 0) {
5931 close(in.fd);
5932 back_exitstatus = waitforjob(in.jp);
5933 }
5934 INT_ON;
5935
5936 /* Eat all trailing newlines */
5937 dest = expdest;
5938 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5939 STUNPUTC(dest);
5940 expdest = dest;
5941
5942 if (quoted == 0)
5943 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005944 TRACE(("evalbackq: size:%d:'%.*s'\n",
5945 (int)((dest - (char *)stackblock()) - startloc),
5946 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005947 stackblock() + startloc));
5948}
5949
Mike Frysinger98c52642009-04-02 10:02:37 +00005950#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005951/*
5952 * Expand arithmetic expression. Backup to start of expression,
5953 * evaluate, place result in (backed up) result, adjust string position.
5954 */
5955static void
5956expari(int quotes)
5957{
5958 char *p, *start;
5959 int begoff;
5960 int flag;
5961 int len;
5962
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005963 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005964
5965 /*
5966 * This routine is slightly over-complicated for
5967 * efficiency. Next we scan backwards looking for the
5968 * start of arithmetic.
5969 */
5970 start = stackblock();
5971 p = expdest - 1;
5972 *p = '\0';
5973 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005974 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005975 int esc;
5976
Denys Vlasenkocd716832009-11-28 22:14:02 +01005977 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005978 p--;
5979#if DEBUG
5980 if (p < start) {
5981 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5982 }
5983#endif
5984 }
5985
5986 esc = esclen(start, p);
5987 if (!(esc % 2)) {
5988 break;
5989 }
5990
5991 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005992 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005993
5994 begoff = p - start;
5995
5996 removerecordregions(begoff);
5997
5998 flag = p[1];
5999
6000 expdest = p;
6001
6002 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006003 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006004
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006005 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006006
6007 if (flag != '"')
6008 recordregion(begoff, begoff + len, 0);
6009}
6010#endif
6011
6012/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006013static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006014
6015/*
6016 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6017 * characters to allow for further processing. Otherwise treat
6018 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006019 *
6020 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6021 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6022 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006023 */
6024static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006025argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006026{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006027 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006028 '=',
6029 ':',
6030 CTLQUOTEMARK,
6031 CTLENDVAR,
6032 CTLESC,
6033 CTLVAR,
6034 CTLBACKQ,
6035 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006036#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037 CTLENDARI,
6038#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006039 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006040 };
6041 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006042 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6043 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006044 int inquotes;
6045 size_t length;
6046 int startloc;
6047
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006048 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006049 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006050 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006051 reject++;
6052 }
6053 inquotes = 0;
6054 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006055 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006056 char *q;
6057
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006058 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006059 tilde:
6060 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006061 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006062 q++;
6063 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006064 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 }
6066 start:
6067 startloc = expdest - (char *)stackblock();
6068 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006069 unsigned char c;
6070
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006071 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006072 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006073 if (c) {
6074 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006075 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006076 ) {
6077 /* c == '=' || c == ':' || c == CTLENDARI */
6078 length++;
6079 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006080 }
6081 if (length > 0) {
6082 int newloc;
6083 expdest = stack_nputstr(p, length, expdest);
6084 newloc = expdest - (char *)stackblock();
6085 if (breakall && !inquotes && newloc > startloc) {
6086 recordregion(startloc, newloc, 0);
6087 }
6088 startloc = newloc;
6089 }
6090 p += length + 1;
6091 length = 0;
6092
6093 switch (c) {
6094 case '\0':
6095 goto breakloop;
6096 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006097 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006098 p--;
6099 continue;
6100 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006101 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006102 reject++;
6103 /* fall through */
6104 case ':':
6105 /*
6106 * sort of a hack - expand tildes in variable
6107 * assignments (after the first '=' and after ':'s).
6108 */
6109 if (*--p == '~') {
6110 goto tilde;
6111 }
6112 continue;
6113 }
6114
6115 switch (c) {
6116 case CTLENDVAR: /* ??? */
6117 goto breakloop;
6118 case CTLQUOTEMARK:
6119 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006120 if (!inquotes
6121 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006122 && ( p[4] == (char)CTLQUOTEMARK
6123 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006124 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006125 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006126 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006127 goto start;
6128 }
6129 inquotes = !inquotes;
6130 addquote:
6131 if (quotes) {
6132 p--;
6133 length++;
6134 startloc++;
6135 }
6136 break;
6137 case CTLESC:
6138 startloc++;
6139 length++;
6140 goto addquote;
6141 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006142 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006143 p = evalvar(p, flags, var_str_list);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006144 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006145 goto start;
6146 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006147 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006148 case CTLBACKQ|CTLQUOTE:
6149 expbackq(argbackq->n, c, quotes);
6150 argbackq = argbackq->next;
6151 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006152#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006153 case CTLENDARI:
6154 p--;
6155 expari(quotes);
6156 goto start;
6157#endif
6158 }
6159 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006160 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006161}
6162
6163static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006164scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6165 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006166{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006167 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 char c;
6169
6170 loc = startp;
6171 loc2 = rmesc;
6172 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006173 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006174 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006175
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006176 c = *loc2;
6177 if (zero) {
6178 *loc2 = '\0';
6179 s = rmesc;
6180 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006181 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006182
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006183 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006184 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006185 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006186 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006187 loc++;
6188 loc++;
6189 loc2++;
6190 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006191 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006192}
6193
6194static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006195scanright(char *startp, char *rmesc, char *rmescend,
6196 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006197{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006198#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6199 int try2optimize = match_at_start;
6200#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006201 int esc = 0;
6202 char *loc;
6203 char *loc2;
6204
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006205 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6206 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6207 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6208 * Logic:
6209 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6210 * and on each iteration they go back two/one char until they reach the beginning.
6211 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6212 */
6213 /* TODO: document in what other circumstances we are called. */
6214
6215 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006216 int match;
6217 char c = *loc2;
6218 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006219 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006220 *loc2 = '\0';
6221 s = rmesc;
6222 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006223 match = pmatch(pattern, s);
6224 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006225 *loc2 = c;
6226 if (match)
6227 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006228#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6229 if (try2optimize) {
6230 /* Maybe we can optimize this:
6231 * if pattern ends with unescaped *, we can avoid checking
6232 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6233 * it wont match truncated "raw_value_of_" strings too.
6234 */
6235 unsigned plen = strlen(pattern);
6236 /* Does it end with "*"? */
6237 if (plen != 0 && pattern[--plen] == '*') {
6238 /* "xxxx*" is not escaped */
6239 /* "xxx\*" is escaped */
6240 /* "xx\\*" is not escaped */
6241 /* "x\\\*" is escaped */
6242 int slashes = 0;
6243 while (plen != 0 && pattern[--plen] == '\\')
6244 slashes++;
6245 if (!(slashes & 1))
6246 break; /* ends with unescaped "*" */
6247 }
6248 try2optimize = 0;
6249 }
6250#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006251 loc--;
6252 if (quotes) {
6253 if (--esc < 0) {
6254 esc = esclen(startp, loc);
6255 }
6256 if (esc % 2) {
6257 esc--;
6258 loc--;
6259 }
6260 }
6261 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006262 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263}
6264
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006265static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266static void
6267varunset(const char *end, const char *var, const char *umsg, int varflags)
6268{
6269 const char *msg;
6270 const char *tail;
6271
6272 tail = nullstr;
6273 msg = "parameter not set";
6274 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006275 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006276 if (varflags & VSNUL)
6277 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006278 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006279 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006280 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006282 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006283}
6284
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006285#if ENABLE_ASH_BASH_COMPAT
6286static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006287parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006288{
6289 char *idx, *repl = NULL;
6290 unsigned char c;
6291
Denys Vlasenko16149002010-08-06 22:06:21 +02006292 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006293 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006294 idx = arg;
6295 while (1) {
6296 c = *arg;
6297 if (!c)
6298 break;
6299 if (c == '/') {
6300 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006301 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006302 repl = idx + 1;
6303 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006304 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006305 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006306 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006307 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006308 /*
6309 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6310 * The result is a_\_z_c (not a\_\_z_c)!
6311 *
6312 * Enable debug prints in this function and you'll see:
6313 * ash: arg:'\\b/_\\_z_' varflags:d
6314 * ash: pattern:'\\b' repl:'_\_z_'
6315 * That is, \\b is interpreted as \\b, but \\_ as \_!
6316 * IOW: search pattern and replace string treat backslashes
6317 * differently! That is the reason why we check repl below:
6318 */
6319 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6320 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006321 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006322 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006323 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006324
6325 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006326}
6327#endif /* ENABLE_ASH_BASH_COMPAT */
6328
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006329static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006330subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006331 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006333 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006334 char *startp;
6335 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006337 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006338 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006339 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006340 int saveherefd = herefd;
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02006341 int amount, resetloc;
6342 IF_ASH_BASH_COMPAT(int workloc;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006343 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006344 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006345
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006346 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6347 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006348
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006349 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006350 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6351 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006352 STPUTC('\0', expdest);
6353 herefd = saveherefd;
6354 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006355 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356
6357 switch (subtype) {
6358 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006359 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006360 amount = startp - expdest;
6361 STADJUST(amount, expdest);
6362 return startp;
6363
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006364 case VSQUESTION:
6365 varunset(p, varname, startp, varflags);
6366 /* NOTREACHED */
6367
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006368#if ENABLE_ASH_BASH_COMPAT
6369 case VSSUBSTR:
6370 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006371 /* Read POS in ${var:POS:LEN} */
6372 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006373 len = str - startp - 1;
6374
6375 /* *loc != '\0', guaranteed by parser */
6376 if (quotes) {
6377 char *ptr;
6378
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006379 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006380 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006381 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006382 len--;
6383 ptr++;
6384 }
6385 }
6386 }
6387 orig_len = len;
6388
6389 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006390 /* ${var::LEN} */
6391 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006392 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006393 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006394 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006395 while (*loc && *loc != ':') {
6396 /* TODO?
6397 * bash complains on: var=qwe; echo ${var:1a:123}
6398 if (!isdigit(*loc))
6399 ash_msg_and_raise_error(msg_illnum, str);
6400 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006401 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006402 }
6403 if (*loc++ == ':') {
6404 len = number(loc);
6405 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006406 }
6407 if (pos >= orig_len) {
6408 pos = 0;
6409 len = 0;
6410 }
6411 if (len > (orig_len - pos))
6412 len = orig_len - pos;
6413
6414 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006415 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006416 str++;
6417 }
6418 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006419 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006420 *loc++ = *str++;
6421 *loc++ = *str++;
6422 }
6423 *loc = '\0';
6424 amount = loc - expdest;
6425 STADJUST(amount, expdest);
6426 return loc;
6427#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006428 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006429
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006430 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006431
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006432 /* We'll comeback here if we grow the stack while handling
6433 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6434 * stack will need rebasing, and we'll need to remove our work
6435 * areas each time
6436 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006437 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006438
6439 amount = expdest - ((char *)stackblock() + resetloc);
6440 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006441 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006442
6443 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006444 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006445 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006446 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006447 if (rmesc != startp) {
6448 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006449 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006450 }
6451 }
6452 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006453 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006454 preglob(str, varflags & VSQUOTE, 0);
6455
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006456#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02006457 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006458 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006459 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006461 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006462 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006463 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006464 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006465 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006466 }
6467
6468 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006469 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006470 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006471
6472 len = 0;
6473 idx = startp;
6474 end = str - 1;
6475 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006476 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006477 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006478 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006479 if (!loc) {
6480 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006481 char *restart_detect = stackblock();
6482 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006483 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006484 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006485 idx++;
6486 len++;
6487 STPUTC(*idx, expdest);
6488 }
6489 if (stackblock() != restart_detect)
6490 goto restart;
6491 idx++;
6492 len++;
6493 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006494 /* continue; - prone to quadratic behavior, smarter code: */
6495 if (idx >= end)
6496 break;
6497 if (str[0] == '*') {
6498 /* Pattern is "*foo". If "*foo" does not match "long_string",
6499 * it would never match "ong_string" etc, no point in trying.
6500 */
6501 goto skip_matching;
6502 }
6503 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006504 }
6505
6506 if (subtype == VSREPLACEALL) {
6507 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006508 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006509 idx++;
6510 idx++;
6511 rmesc++;
6512 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006513 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006514 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006515 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006516
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006517 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006518 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006519 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006520 if (quotes && *loc == '\\') {
6521 STPUTC(CTLESC, expdest);
6522 len++;
6523 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006524 STPUTC(*loc, expdest);
6525 if (stackblock() != restart_detect)
6526 goto restart;
6527 len++;
6528 }
6529
6530 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006531 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006532 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006533 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006534 STPUTC(*idx, expdest);
6535 if (stackblock() != restart_detect)
6536 goto restart;
6537 len++;
6538 idx++;
6539 }
6540 break;
6541 }
6542 }
6543
6544 /* We've put the replaced text into a buffer at workloc, now
6545 * move it to the right place and adjust the stack.
6546 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006547 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006548 startp = (char *)stackblock() + startloc;
6549 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006550 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006551 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006552 STADJUST(-amount, expdest);
6553 return startp;
6554 }
6555#endif /* ENABLE_ASH_BASH_COMPAT */
6556
6557 subtype -= VSTRIMRIGHT;
6558#if DEBUG
6559 if (subtype < 0 || subtype > 7)
6560 abort();
6561#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006562 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006563 zero = subtype >> 1;
6564 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6565 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6566
6567 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6568 if (loc) {
6569 if (zero) {
6570 memmove(startp, loc, str - loc);
6571 loc = startp + (str - loc) - 1;
6572 }
6573 *loc = '\0';
6574 amount = loc - expdest;
6575 STADJUST(amount, expdest);
6576 }
6577 return loc;
6578}
6579
6580/*
6581 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006582 * name parameter (examples):
6583 * ash -c 'echo $1' name:'1='
6584 * ash -c 'echo $qwe' name:'qwe='
6585 * ash -c 'echo $$' name:'$='
6586 * ash -c 'echo ${$}' name:'$='
6587 * ash -c 'echo ${$##q}' name:'$=q'
6588 * ash -c 'echo ${#$}' name:'$='
6589 * note: examples with bad shell syntax:
6590 * ash -c 'echo ${#$1}' name:'$=1'
6591 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006592 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006593static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006594varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006595{
Mike Frysinger98c52642009-04-02 10:02:37 +00006596 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006597 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006598 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006599 int sepq = 0;
6600 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006601 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006602 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006603 int quoted = varflags & VSQUOTE;
6604 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006605
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006606 switch (*name) {
6607 case '$':
6608 num = rootpid;
6609 goto numvar;
6610 case '?':
6611 num = exitstatus;
6612 goto numvar;
6613 case '#':
6614 num = shellparam.nparam;
6615 goto numvar;
6616 case '!':
6617 num = backgndpid;
6618 if (num == 0)
6619 return -1;
6620 numvar:
6621 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006622 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006623 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006624 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006625 for (i = NOPTS - 1; i >= 0; i--) {
6626 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006627 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006628 len++;
6629 }
6630 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006631 check_1char_name:
6632#if 0
6633 /* handles cases similar to ${#$1} */
6634 if (name[2] != '\0')
6635 raise_error_syntax("bad substitution");
6636#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006637 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006638 case '@': {
6639 char **ap;
6640 int sep;
6641
6642 if (quoted && (flags & EXP_FULL)) {
6643 /* note: this is not meant as PEOF value */
6644 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006645 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006646 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006647 /* fall through */
6648 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006649 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006650 i = SIT(sep, syntax);
6651 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006652 sepq = 1;
6653 param:
6654 ap = shellparam.p;
6655 if (!ap)
6656 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006657 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006658 size_t partlen;
6659
6660 partlen = strlen(p);
6661 len += partlen;
6662
6663 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6664 memtodest(p, partlen, syntax, quotes);
6665
6666 if (*ap && sep) {
6667 char *q;
6668
6669 len++;
6670 if (subtype == VSPLUS || subtype == VSLENGTH) {
6671 continue;
6672 }
6673 q = expdest;
6674 if (sepq)
6675 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006676 /* note: may put NUL despite sep != 0
6677 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006678 STPUTC(sep, q);
6679 expdest = q;
6680 }
6681 }
6682 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006683 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006684 case '0':
6685 case '1':
6686 case '2':
6687 case '3':
6688 case '4':
6689 case '5':
6690 case '6':
6691 case '7':
6692 case '8':
6693 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006694 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006695 if (num < 0 || num > shellparam.nparam)
6696 return -1;
6697 p = num ? shellparam.p[num - 1] : arg0;
6698 goto value;
6699 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006700 /* NB: name has form "VAR=..." */
6701
6702 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6703 * which should be considered before we check variables. */
6704 if (var_str_list) {
6705 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6706 p = NULL;
6707 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006708 char *str, *eq;
6709 str = var_str_list->text;
6710 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006711 if (!eq) /* stop at first non-assignment */
6712 break;
6713 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006714 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006715 && strncmp(str, name, name_len) == 0
6716 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006717 p = eq;
6718 /* goto value; - WRONG! */
6719 /* think "A=1 A=2 B=$A" */
6720 }
6721 var_str_list = var_str_list->next;
6722 } while (var_str_list);
6723 if (p)
6724 goto value;
6725 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006726 p = lookupvar(name);
6727 value:
6728 if (!p)
6729 return -1;
6730
6731 len = strlen(p);
6732 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6733 memtodest(p, len, syntax, quotes);
6734 return len;
6735 }
6736
6737 if (subtype == VSPLUS || subtype == VSLENGTH)
6738 STADJUST(-len, expdest);
6739 return len;
6740}
6741
6742/*
6743 * Expand a variable, and return a pointer to the next character in the
6744 * input string.
6745 */
6746static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006747evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006749 char varflags;
6750 char subtype;
6751 char quoted;
6752 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006753 char *var;
6754 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006755 int startloc;
6756 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006758 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006759 subtype = varflags & VSTYPE;
6760 quoted = varflags & VSQUOTE;
6761 var = p;
6762 easy = (!quoted || (*var == '@' && shellparam.nparam));
6763 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006764 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006765
6766 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006767 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006768 if (varflags & VSNUL)
6769 varlen--;
6770
6771 if (subtype == VSPLUS) {
6772 varlen = -1 - varlen;
6773 goto vsplus;
6774 }
6775
6776 if (subtype == VSMINUS) {
6777 vsplus:
6778 if (varlen < 0) {
6779 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006780 p,
6781 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006782 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006783 );
6784 goto end;
6785 }
6786 if (easy)
6787 goto record;
6788 goto end;
6789 }
6790
6791 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6792 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006793 if (subevalvar(p, var, /* strloc: */ 0,
6794 subtype, startloc, varflags,
6795 /* quotes: */ 0,
6796 var_str_list)
6797 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798 varflags &= ~VSNUL;
6799 /*
6800 * Remove any recorded regions beyond
6801 * start of variable
6802 */
6803 removerecordregions(startloc);
6804 goto again;
6805 }
6806 goto end;
6807 }
6808 if (easy)
6809 goto record;
6810 goto end;
6811 }
6812
6813 if (varlen < 0 && uflag)
6814 varunset(p, var, 0, 0);
6815
6816 if (subtype == VSLENGTH) {
6817 cvtnum(varlen > 0 ? varlen : 0);
6818 goto record;
6819 }
6820
6821 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006822 if (easy)
6823 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006824 goto end;
6825 }
6826
6827#if DEBUG
6828 switch (subtype) {
6829 case VSTRIMLEFT:
6830 case VSTRIMLEFTMAX:
6831 case VSTRIMRIGHT:
6832 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006833#if ENABLE_ASH_BASH_COMPAT
6834 case VSSUBSTR:
6835 case VSREPLACE:
6836 case VSREPLACEALL:
6837#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006838 break;
6839 default:
6840 abort();
6841 }
6842#endif
6843
6844 if (varlen >= 0) {
6845 /*
6846 * Terminate the string and start recording the pattern
6847 * right after it
6848 */
6849 STPUTC('\0', expdest);
6850 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006851 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006852 startloc, varflags,
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006853 /* quotes: */ flags & (EXP_FULL | EXP_CASE | EXP_REDIR),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006854 var_str_list)
6855 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006856 int amount = expdest - (
6857 (char *)stackblock() + patloc - 1
6858 );
6859 STADJUST(-amount, expdest);
6860 }
6861 /* Remove any recorded regions beyond start of variable */
6862 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006863 record:
6864 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006865 }
6866
6867 end:
6868 if (subtype != VSNORMAL) { /* skip to end of alternative */
6869 int nesting = 1;
6870 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006871 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006872 if (c == CTLESC)
6873 p++;
6874 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6875 if (varlen >= 0)
6876 argbackq = argbackq->next;
6877 } else if (c == CTLVAR) {
6878 if ((*p++ & VSTYPE) != VSNORMAL)
6879 nesting++;
6880 } else if (c == CTLENDVAR) {
6881 if (--nesting == 0)
6882 break;
6883 }
6884 }
6885 }
6886 return p;
6887}
6888
6889/*
6890 * Break the argument string into pieces based upon IFS and add the
6891 * strings to the argument list. The regions of the string to be
6892 * searched for IFS characters have been stored by recordregion.
6893 */
6894static void
6895ifsbreakup(char *string, struct arglist *arglist)
6896{
6897 struct ifsregion *ifsp;
6898 struct strlist *sp;
6899 char *start;
6900 char *p;
6901 char *q;
6902 const char *ifs, *realifs;
6903 int ifsspc;
6904 int nulonly;
6905
6906 start = string;
6907 if (ifslastp != NULL) {
6908 ifsspc = 0;
6909 nulonly = 0;
6910 realifs = ifsset() ? ifsval() : defifs;
6911 ifsp = &ifsfirst;
6912 do {
6913 p = string + ifsp->begoff;
6914 nulonly = ifsp->nulonly;
6915 ifs = nulonly ? nullstr : realifs;
6916 ifsspc = 0;
6917 while (p < string + ifsp->endoff) {
6918 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006919 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006920 p++;
6921 if (!strchr(ifs, *p)) {
6922 p++;
6923 continue;
6924 }
6925 if (!nulonly)
6926 ifsspc = (strchr(defifs, *p) != NULL);
6927 /* Ignore IFS whitespace at start */
6928 if (q == start && ifsspc) {
6929 p++;
6930 start = p;
6931 continue;
6932 }
6933 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006934 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006935 sp->text = start;
6936 *arglist->lastp = sp;
6937 arglist->lastp = &sp->next;
6938 p++;
6939 if (!nulonly) {
6940 for (;;) {
6941 if (p >= string + ifsp->endoff) {
6942 break;
6943 }
6944 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006945 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006946 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006947 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006948 p = q;
6949 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006950 }
6951 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006952 if (ifsspc) {
6953 p++;
6954 ifsspc = 0;
6955 } else {
6956 p = q;
6957 break;
6958 }
6959 } else
6960 p++;
6961 }
6962 }
6963 start = p;
6964 } /* while */
6965 ifsp = ifsp->next;
6966 } while (ifsp != NULL);
6967 if (nulonly)
6968 goto add;
6969 }
6970
6971 if (!*start)
6972 return;
6973
6974 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006975 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006976 sp->text = start;
6977 *arglist->lastp = sp;
6978 arglist->lastp = &sp->next;
6979}
6980
6981static void
6982ifsfree(void)
6983{
6984 struct ifsregion *p;
6985
6986 INT_OFF;
6987 p = ifsfirst.next;
6988 do {
6989 struct ifsregion *ifsp;
6990 ifsp = p->next;
6991 free(p);
6992 p = ifsp;
6993 } while (p);
6994 ifslastp = NULL;
6995 ifsfirst.next = NULL;
6996 INT_ON;
6997}
6998
6999/*
7000 * Add a file name to the list.
7001 */
7002static void
7003addfname(const char *name)
7004{
7005 struct strlist *sp;
7006
Denis Vlasenko597906c2008-02-20 16:38:54 +00007007 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007008 sp->text = ststrdup(name);
7009 *exparg.lastp = sp;
7010 exparg.lastp = &sp->next;
7011}
7012
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007013/*
7014 * Do metacharacter (i.e. *, ?, [...]) expansion.
7015 */
7016static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007017expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007018{
7019 char *p;
7020 const char *cp;
7021 char *start;
7022 char *endname;
7023 int metaflag;
7024 struct stat statb;
7025 DIR *dirp;
7026 struct dirent *dp;
7027 int atend;
7028 int matchdot;
7029
7030 metaflag = 0;
7031 start = name;
7032 for (p = name; *p; p++) {
7033 if (*p == '*' || *p == '?')
7034 metaflag = 1;
7035 else if (*p == '[') {
7036 char *q = p + 1;
7037 if (*q == '!')
7038 q++;
7039 for (;;) {
7040 if (*q == '\\')
7041 q++;
7042 if (*q == '/' || *q == '\0')
7043 break;
7044 if (*++q == ']') {
7045 metaflag = 1;
7046 break;
7047 }
7048 }
7049 } else if (*p == '\\')
7050 p++;
7051 else if (*p == '/') {
7052 if (metaflag)
7053 goto out;
7054 start = p + 1;
7055 }
7056 }
7057 out:
7058 if (metaflag == 0) { /* we've reached the end of the file name */
7059 if (enddir != expdir)
7060 metaflag++;
7061 p = name;
7062 do {
7063 if (*p == '\\')
7064 p++;
7065 *enddir++ = *p;
7066 } while (*p++);
7067 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7068 addfname(expdir);
7069 return;
7070 }
7071 endname = p;
7072 if (name < start) {
7073 p = name;
7074 do {
7075 if (*p == '\\')
7076 p++;
7077 *enddir++ = *p++;
7078 } while (p < start);
7079 }
7080 if (enddir == expdir) {
7081 cp = ".";
7082 } else if (enddir == expdir + 1 && *expdir == '/') {
7083 cp = "/";
7084 } else {
7085 cp = expdir;
7086 enddir[-1] = '\0';
7087 }
7088 dirp = opendir(cp);
7089 if (dirp == NULL)
7090 return;
7091 if (enddir != expdir)
7092 enddir[-1] = '/';
7093 if (*endname == 0) {
7094 atend = 1;
7095 } else {
7096 atend = 0;
7097 *endname++ = '\0';
7098 }
7099 matchdot = 0;
7100 p = start;
7101 if (*p == '\\')
7102 p++;
7103 if (*p == '.')
7104 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007105 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007106 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007107 continue;
7108 if (pmatch(start, dp->d_name)) {
7109 if (atend) {
7110 strcpy(enddir, dp->d_name);
7111 addfname(expdir);
7112 } else {
7113 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7114 continue;
7115 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007116 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007117 }
7118 }
7119 }
7120 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007121 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007122 endname[-1] = '/';
7123}
7124
7125static struct strlist *
7126msort(struct strlist *list, int len)
7127{
7128 struct strlist *p, *q = NULL;
7129 struct strlist **lpp;
7130 int half;
7131 int n;
7132
7133 if (len <= 1)
7134 return list;
7135 half = len >> 1;
7136 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007137 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007138 q = p;
7139 p = p->next;
7140 }
7141 q->next = NULL; /* terminate first half of list */
7142 q = msort(list, half); /* sort first half of list */
7143 p = msort(p, len - half); /* sort second half */
7144 lpp = &list;
7145 for (;;) {
7146#if ENABLE_LOCALE_SUPPORT
7147 if (strcoll(p->text, q->text) < 0)
7148#else
7149 if (strcmp(p->text, q->text) < 0)
7150#endif
7151 {
7152 *lpp = p;
7153 lpp = &p->next;
7154 p = *lpp;
7155 if (p == NULL) {
7156 *lpp = q;
7157 break;
7158 }
7159 } else {
7160 *lpp = q;
7161 lpp = &q->next;
7162 q = *lpp;
7163 if (q == NULL) {
7164 *lpp = p;
7165 break;
7166 }
7167 }
7168 }
7169 return list;
7170}
7171
7172/*
7173 * Sort the results of file name expansion. It calculates the number of
7174 * strings to sort and then calls msort (short for merge sort) to do the
7175 * work.
7176 */
7177static struct strlist *
7178expsort(struct strlist *str)
7179{
7180 int len;
7181 struct strlist *sp;
7182
7183 len = 0;
7184 for (sp = str; sp; sp = sp->next)
7185 len++;
7186 return msort(str, len);
7187}
7188
7189static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007190expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007191{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007192 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007193 '*', '?', '[', 0
7194 };
7195 /* TODO - EXP_REDIR */
7196
7197 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007198 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007199 struct strlist **savelastp;
7200 struct strlist *sp;
7201 char *p;
7202
7203 if (fflag)
7204 goto nometa;
7205 if (!strpbrk(str->text, metachars))
7206 goto nometa;
7207 savelastp = exparg.lastp;
7208
7209 INT_OFF;
7210 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7211 {
7212 int i = strlen(str->text);
7213 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7214 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007215 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007216 free(expdir);
7217 if (p != str->text)
7218 free(p);
7219 INT_ON;
7220 if (exparg.lastp == savelastp) {
7221 /*
7222 * no matches
7223 */
7224 nometa:
7225 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007226 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007227 exparg.lastp = &str->next;
7228 } else {
7229 *exparg.lastp = NULL;
7230 *savelastp = sp = expsort(*savelastp);
7231 while (sp->next != NULL)
7232 sp = sp->next;
7233 exparg.lastp = &sp->next;
7234 }
7235 str = str->next;
7236 }
7237}
7238
7239/*
7240 * Perform variable substitution and command substitution on an argument,
7241 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7242 * perform splitting and file name expansion. When arglist is NULL, perform
7243 * here document expansion.
7244 */
7245static void
7246expandarg(union node *arg, struct arglist *arglist, int flag)
7247{
7248 struct strlist *sp;
7249 char *p;
7250
7251 argbackq = arg->narg.backquote;
7252 STARTSTACKSTR(expdest);
7253 ifsfirst.next = NULL;
7254 ifslastp = NULL;
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007255 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007256 argstr(arg->narg.text, flag,
7257 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007258 p = _STPUTC('\0', expdest);
7259 expdest = p - 1;
7260 if (arglist == NULL) {
7261 return; /* here document expanded */
7262 }
7263 p = grabstackstr(p);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007264 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007265 exparg.lastp = &exparg.list;
7266 /*
7267 * TODO - EXP_REDIR
7268 */
7269 if (flag & EXP_FULL) {
7270 ifsbreakup(p, &exparg);
7271 *exparg.lastp = NULL;
7272 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007273 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007274 } else {
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007275 if (flag & EXP_REDIR) { /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007276 rmescapes(p, 0);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007277 TRACE(("expandarg: rmescapes:'%s'\n", p));
7278 }
Denis Vlasenko597906c2008-02-20 16:38:54 +00007279 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007280 sp->text = p;
7281 *exparg.lastp = sp;
7282 exparg.lastp = &sp->next;
7283 }
7284 if (ifsfirst.next)
7285 ifsfree();
7286 *exparg.lastp = NULL;
7287 if (exparg.list) {
7288 *arglist->lastp = exparg.list;
7289 arglist->lastp = exparg.lastp;
7290 }
7291}
7292
7293/*
7294 * Expand shell variables and backquotes inside a here document.
7295 */
7296static void
7297expandhere(union node *arg, int fd)
7298{
7299 herefd = fd;
7300 expandarg(arg, (struct arglist *)NULL, 0);
7301 full_write(fd, stackblock(), expdest - (char *)stackblock());
7302}
7303
7304/*
7305 * Returns true if the pattern matches the string.
7306 */
7307static int
7308patmatch(char *pattern, const char *string)
7309{
7310 return pmatch(preglob(pattern, 0, 0), string);
7311}
7312
7313/*
7314 * See if a pattern matches in a case statement.
7315 */
7316static int
7317casematch(union node *pattern, char *val)
7318{
7319 struct stackmark smark;
7320 int result;
7321
7322 setstackmark(&smark);
7323 argbackq = pattern->narg.backquote;
7324 STARTSTACKSTR(expdest);
7325 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007326 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7327 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007328 STACKSTRNUL(expdest);
7329 result = patmatch(stackblock(), val);
7330 popstackmark(&smark);
7331 return result;
7332}
7333
7334
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335/* ============ find_command */
7336
7337struct builtincmd {
7338 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007339 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007340 /* unsigned flags; */
7341};
7342#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007343/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007344 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007345#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007346#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007347
7348struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007349 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350 union param {
7351 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007352 /* index >= 0 for commands without path (slashes) */
7353 /* (TODO: what exactly does the value mean? PATH position?) */
7354 /* index == -1 for commands with slashes */
7355 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007356 const struct builtincmd *cmd;
7357 struct funcnode *func;
7358 } u;
7359};
7360/* values of cmdtype */
7361#define CMDUNKNOWN -1 /* no entry in table for command */
7362#define CMDNORMAL 0 /* command is an executable program */
7363#define CMDFUNCTION 1 /* command is a shell function */
7364#define CMDBUILTIN 2 /* command is a shell builtin */
7365
7366/* action to find_command() */
7367#define DO_ERR 0x01 /* prints errors */
7368#define DO_ABS 0x02 /* checks absolute paths */
7369#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7370#define DO_ALTPATH 0x08 /* using alternate path */
7371#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7372
7373static void find_command(char *, struct cmdentry *, int, const char *);
7374
7375
7376/* ============ Hashing commands */
7377
7378/*
7379 * When commands are first encountered, they are entered in a hash table.
7380 * This ensures that a full path search will not have to be done for them
7381 * on each invocation.
7382 *
7383 * We should investigate converting to a linear search, even though that
7384 * would make the command name "hash" a misnomer.
7385 */
7386
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007387struct tblentry {
7388 struct tblentry *next; /* next entry in hash chain */
7389 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007390 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007391 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007392 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007393};
7394
Denis Vlasenko01631112007-12-16 17:20:38 +00007395static struct tblentry **cmdtable;
7396#define INIT_G_cmdtable() do { \
7397 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7398} while (0)
7399
7400static int builtinloc = -1; /* index in path of %builtin, or -1 */
7401
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007402
7403static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007404tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007405{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007406#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007407 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007408 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007409 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007410 while (*envp)
7411 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007412 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007413 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007414 /* re-exec ourselves with the new arguments */
7415 execve(bb_busybox_exec_path, argv, envp);
7416 /* If they called chroot or otherwise made the binary no longer
7417 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007418 }
7419#endif
7420
7421 repeat:
7422#ifdef SYSV
7423 do {
7424 execve(cmd, argv, envp);
7425 } while (errno == EINTR);
7426#else
7427 execve(cmd, argv, envp);
7428#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007429 if (cmd == (char*) bb_busybox_exec_path) {
7430 /* We already visited ENOEXEC branch below, don't do it again */
7431//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007433 return;
7434 }
7435 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007436 /* Run "cmd" as a shell script:
7437 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7438 * "If the execve() function fails with ENOEXEC, the shell
7439 * shall execute a command equivalent to having a shell invoked
7440 * with the command name as its first operand,
7441 * with any remaining arguments passed to the new shell"
7442 *
7443 * That is, do not use $SHELL, user's shell, or /bin/sh;
7444 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01007445 *
7446 * Note that bash reads ~80 chars of the file, and if it sees
7447 * a zero byte before it sees newline, it doesn't try to
7448 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01007449 * message and exit code 126. For one, this prevents attempts
7450 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007451 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007452 char **ap;
7453 char **new;
7454
7455 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007456 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007457 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7458 new[0] = (char*) "ash";
7459 new[1] = cmd;
7460 ap = new + 2;
7461 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007462 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007463 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007464 argv = new;
7465 goto repeat;
7466 }
7467}
7468
7469/*
7470 * Exec a program. Never returns. If you change this routine, you may
7471 * have to change the find_command routine as well.
7472 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007473static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007474static void
7475shellexec(char **argv, const char *path, int idx)
7476{
7477 char *cmdname;
7478 int e;
7479 char **envp;
7480 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007481 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007482
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007483 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007484 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007485 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007486#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007487 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007488#endif
7489 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007490 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007491 if (applet_no >= 0) {
7492 /* We tried execing ourself, but it didn't work.
7493 * Maybe /proc/self/exe doesn't exist?
7494 * Try $PATH search.
7495 */
7496 goto try_PATH;
7497 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007498 e = errno;
7499 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01007500 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007501 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007502 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007503 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007504 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007505 if (errno != ENOENT && errno != ENOTDIR)
7506 e = errno;
7507 }
7508 stunalloc(cmdname);
7509 }
7510 }
7511
7512 /* Map to POSIX errors */
7513 switch (e) {
7514 case EACCES:
7515 exerrno = 126;
7516 break;
7517 case ENOENT:
7518 exerrno = 127;
7519 break;
7520 default:
7521 exerrno = 2;
7522 break;
7523 }
7524 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007525 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7526 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007527 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7528 /* NOTREACHED */
7529}
7530
7531static void
7532printentry(struct tblentry *cmdp)
7533{
7534 int idx;
7535 const char *path;
7536 char *name;
7537
7538 idx = cmdp->param.index;
7539 path = pathval();
7540 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007541 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007542 stunalloc(name);
7543 } while (--idx >= 0);
7544 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7545}
7546
7547/*
7548 * Clear out command entries. The argument specifies the first entry in
7549 * PATH which has changed.
7550 */
7551static void
7552clearcmdentry(int firstchange)
7553{
7554 struct tblentry **tblp;
7555 struct tblentry **pp;
7556 struct tblentry *cmdp;
7557
7558 INT_OFF;
7559 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7560 pp = tblp;
7561 while ((cmdp = *pp) != NULL) {
7562 if ((cmdp->cmdtype == CMDNORMAL &&
7563 cmdp->param.index >= firstchange)
7564 || (cmdp->cmdtype == CMDBUILTIN &&
7565 builtinloc >= firstchange)
7566 ) {
7567 *pp = cmdp->next;
7568 free(cmdp);
7569 } else {
7570 pp = &cmdp->next;
7571 }
7572 }
7573 }
7574 INT_ON;
7575}
7576
7577/*
7578 * Locate a command in the command hash table. If "add" is nonzero,
7579 * add the command to the table if it is not already present. The
7580 * variable "lastcmdentry" is set to point to the address of the link
7581 * pointing to the entry, so that delete_cmd_entry can delete the
7582 * entry.
7583 *
7584 * Interrupts must be off if called with add != 0.
7585 */
7586static struct tblentry **lastcmdentry;
7587
7588static struct tblentry *
7589cmdlookup(const char *name, int add)
7590{
7591 unsigned int hashval;
7592 const char *p;
7593 struct tblentry *cmdp;
7594 struct tblentry **pp;
7595
7596 p = name;
7597 hashval = (unsigned char)*p << 4;
7598 while (*p)
7599 hashval += (unsigned char)*p++;
7600 hashval &= 0x7FFF;
7601 pp = &cmdtable[hashval % CMDTABLESIZE];
7602 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7603 if (strcmp(cmdp->cmdname, name) == 0)
7604 break;
7605 pp = &cmdp->next;
7606 }
7607 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007608 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7609 + strlen(name)
7610 /* + 1 - already done because
7611 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007612 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007613 cmdp->cmdtype = CMDUNKNOWN;
7614 strcpy(cmdp->cmdname, name);
7615 }
7616 lastcmdentry = pp;
7617 return cmdp;
7618}
7619
7620/*
7621 * Delete the command entry returned on the last lookup.
7622 */
7623static void
7624delete_cmd_entry(void)
7625{
7626 struct tblentry *cmdp;
7627
7628 INT_OFF;
7629 cmdp = *lastcmdentry;
7630 *lastcmdentry = cmdp->next;
7631 if (cmdp->cmdtype == CMDFUNCTION)
7632 freefunc(cmdp->param.func);
7633 free(cmdp);
7634 INT_ON;
7635}
7636
7637/*
7638 * Add a new command entry, replacing any existing command entry for
7639 * the same name - except special builtins.
7640 */
7641static void
7642addcmdentry(char *name, struct cmdentry *entry)
7643{
7644 struct tblentry *cmdp;
7645
7646 cmdp = cmdlookup(name, 1);
7647 if (cmdp->cmdtype == CMDFUNCTION) {
7648 freefunc(cmdp->param.func);
7649 }
7650 cmdp->cmdtype = entry->cmdtype;
7651 cmdp->param = entry->u;
7652 cmdp->rehash = 0;
7653}
7654
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007655static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007656hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007657{
7658 struct tblentry **pp;
7659 struct tblentry *cmdp;
7660 int c;
7661 struct cmdentry entry;
7662 char *name;
7663
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007664 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007665 clearcmdentry(0);
7666 return 0;
7667 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007668
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007669 if (*argptr == NULL) {
7670 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7671 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7672 if (cmdp->cmdtype == CMDNORMAL)
7673 printentry(cmdp);
7674 }
7675 }
7676 return 0;
7677 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007678
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007679 c = 0;
7680 while ((name = *argptr) != NULL) {
7681 cmdp = cmdlookup(name, 0);
7682 if (cmdp != NULL
7683 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007684 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7685 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007686 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007687 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007688 find_command(name, &entry, DO_ERR, pathval());
7689 if (entry.cmdtype == CMDUNKNOWN)
7690 c = 1;
7691 argptr++;
7692 }
7693 return c;
7694}
7695
7696/*
7697 * Called when a cd is done. Marks all commands so the next time they
7698 * are executed they will be rehashed.
7699 */
7700static void
7701hashcd(void)
7702{
7703 struct tblentry **pp;
7704 struct tblentry *cmdp;
7705
7706 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7707 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007708 if (cmdp->cmdtype == CMDNORMAL
7709 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007710 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007711 && builtinloc > 0)
7712 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007713 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007714 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007715 }
7716 }
7717}
7718
7719/*
7720 * Fix command hash table when PATH changed.
7721 * Called before PATH is changed. The argument is the new value of PATH;
7722 * pathval() still returns the old value at this point.
7723 * Called with interrupts off.
7724 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007725static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007726changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007727{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007728 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007729 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007730 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007731 int idx_bltin;
7732
7733 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007734 firstchange = 9999; /* assume no change */
7735 idx = 0;
7736 idx_bltin = -1;
7737 for (;;) {
7738 if (*old != *new) {
7739 firstchange = idx;
7740 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007741 || (*old == ':' && *new == '\0')
7742 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007743 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007744 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007745 old = new; /* ignore subsequent differences */
7746 }
7747 if (*new == '\0')
7748 break;
7749 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7750 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007751 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007752 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007753 new++;
7754 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007755 }
7756 if (builtinloc < 0 && idx_bltin >= 0)
7757 builtinloc = idx_bltin; /* zap builtins */
7758 if (builtinloc >= 0 && idx_bltin < 0)
7759 firstchange = 0;
7760 clearcmdentry(firstchange);
7761 builtinloc = idx_bltin;
7762}
7763
7764#define TEOF 0
7765#define TNL 1
7766#define TREDIR 2
7767#define TWORD 3
7768#define TSEMI 4
7769#define TBACKGND 5
7770#define TAND 6
7771#define TOR 7
7772#define TPIPE 8
7773#define TLP 9
7774#define TRP 10
7775#define TENDCASE 11
7776#define TENDBQUOTE 12
7777#define TNOT 13
7778#define TCASE 14
7779#define TDO 15
7780#define TDONE 16
7781#define TELIF 17
7782#define TELSE 18
7783#define TESAC 19
7784#define TFI 20
7785#define TFOR 21
7786#define TIF 22
7787#define TIN 23
7788#define TTHEN 24
7789#define TUNTIL 25
7790#define TWHILE 26
7791#define TBEGIN 27
7792#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007793typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007794
7795/* first char is indicating which tokens mark the end of a list */
7796static const char *const tokname_array[] = {
7797 "\1end of file",
7798 "\0newline",
7799 "\0redirection",
7800 "\0word",
7801 "\0;",
7802 "\0&",
7803 "\0&&",
7804 "\0||",
7805 "\0|",
7806 "\0(",
7807 "\1)",
7808 "\1;;",
7809 "\1`",
7810#define KWDOFFSET 13
7811 /* the following are keywords */
7812 "\0!",
7813 "\0case",
7814 "\1do",
7815 "\1done",
7816 "\1elif",
7817 "\1else",
7818 "\1esac",
7819 "\1fi",
7820 "\0for",
7821 "\0if",
7822 "\0in",
7823 "\1then",
7824 "\0until",
7825 "\0while",
7826 "\0{",
7827 "\1}",
7828};
7829
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007830/* Wrapper around strcmp for qsort/bsearch/... */
7831static int
7832pstrcmp(const void *a, const void *b)
7833{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007834 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007835}
7836
7837static const char *const *
7838findkwd(const char *s)
7839{
7840 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007841 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7842 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007843}
7844
7845/*
7846 * Locate and print what a word is...
7847 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007848static int
7849describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007850{
7851 struct cmdentry entry;
7852 struct tblentry *cmdp;
7853#if ENABLE_ASH_ALIAS
7854 const struct alias *ap;
7855#endif
7856 const char *path = pathval();
7857
7858 if (describe_command_verbose) {
7859 out1str(command);
7860 }
7861
7862 /* First look at the keywords */
7863 if (findkwd(command)) {
7864 out1str(describe_command_verbose ? " is a shell keyword" : command);
7865 goto out;
7866 }
7867
7868#if ENABLE_ASH_ALIAS
7869 /* Then look at the aliases */
7870 ap = lookupalias(command, 0);
7871 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007872 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007873 out1str("alias ");
7874 printalias(ap);
7875 return 0;
7876 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007877 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007878 goto out;
7879 }
7880#endif
7881 /* Then check if it is a tracked alias */
7882 cmdp = cmdlookup(command, 0);
7883 if (cmdp != NULL) {
7884 entry.cmdtype = cmdp->cmdtype;
7885 entry.u = cmdp->param;
7886 } else {
7887 /* Finally use brute force */
7888 find_command(command, &entry, DO_ABS, path);
7889 }
7890
7891 switch (entry.cmdtype) {
7892 case CMDNORMAL: {
7893 int j = entry.u.index;
7894 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007895 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007896 p = command;
7897 } else {
7898 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007899 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007900 stunalloc(p);
7901 } while (--j >= 0);
7902 }
7903 if (describe_command_verbose) {
7904 out1fmt(" is%s %s",
7905 (cmdp ? " a tracked alias for" : nullstr), p
7906 );
7907 } else {
7908 out1str(p);
7909 }
7910 break;
7911 }
7912
7913 case CMDFUNCTION:
7914 if (describe_command_verbose) {
7915 out1str(" is a shell function");
7916 } else {
7917 out1str(command);
7918 }
7919 break;
7920
7921 case CMDBUILTIN:
7922 if (describe_command_verbose) {
7923 out1fmt(" is a %sshell builtin",
7924 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7925 "special " : nullstr
7926 );
7927 } else {
7928 out1str(command);
7929 }
7930 break;
7931
7932 default:
7933 if (describe_command_verbose) {
7934 out1str(": not found\n");
7935 }
7936 return 127;
7937 }
7938 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007939 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007940 return 0;
7941}
7942
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007943static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007944typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007945{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007946 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007947 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007948 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007949
Denis Vlasenko46846e22007-05-20 13:08:31 +00007950 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007951 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007952 i++;
7953 verbose = 0;
7954 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007955 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007956 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007957 }
7958 return err;
7959}
7960
7961#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007962static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007963commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007964{
7965 int c;
7966 enum {
7967 VERIFY_BRIEF = 1,
7968 VERIFY_VERBOSE = 2,
7969 } verify = 0;
7970
7971 while ((c = nextopt("pvV")) != '\0')
7972 if (c == 'V')
7973 verify |= VERIFY_VERBOSE;
7974 else if (c == 'v')
7975 verify |= VERIFY_BRIEF;
7976#if DEBUG
7977 else if (c != 'p')
7978 abort();
7979#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007980 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7981 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007982 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007983 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007984
7985 return 0;
7986}
7987#endif
7988
7989
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007990/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007991
Denis Vlasenko340299a2008-11-21 10:36:36 +00007992static int funcblocksize; /* size of structures in function */
7993static int funcstringsize; /* size of strings in node */
7994static void *funcblock; /* block to allocate function from */
7995static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007996
Eric Andersencb57d552001-06-28 07:25:16 +00007997/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007998#define EV_EXIT 01 /* exit after evaluating tree */
7999#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00008000#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00008001
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02008002static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008003 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8004 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8005 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8006 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8007 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8008 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8009 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8010 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8011 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8012 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8013 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8014 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8015 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8016 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8017 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8018 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8019 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008020#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008021 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008022#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008023 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8024 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8025 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8026 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8027 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8028 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8029 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8030 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8031 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008032};
8033
8034static void calcsize(union node *n);
8035
8036static void
8037sizenodelist(struct nodelist *lp)
8038{
8039 while (lp) {
8040 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8041 calcsize(lp->n);
8042 lp = lp->next;
8043 }
8044}
8045
8046static void
8047calcsize(union node *n)
8048{
8049 if (n == NULL)
8050 return;
8051 funcblocksize += nodesize[n->type];
8052 switch (n->type) {
8053 case NCMD:
8054 calcsize(n->ncmd.redirect);
8055 calcsize(n->ncmd.args);
8056 calcsize(n->ncmd.assign);
8057 break;
8058 case NPIPE:
8059 sizenodelist(n->npipe.cmdlist);
8060 break;
8061 case NREDIR:
8062 case NBACKGND:
8063 case NSUBSHELL:
8064 calcsize(n->nredir.redirect);
8065 calcsize(n->nredir.n);
8066 break;
8067 case NAND:
8068 case NOR:
8069 case NSEMI:
8070 case NWHILE:
8071 case NUNTIL:
8072 calcsize(n->nbinary.ch2);
8073 calcsize(n->nbinary.ch1);
8074 break;
8075 case NIF:
8076 calcsize(n->nif.elsepart);
8077 calcsize(n->nif.ifpart);
8078 calcsize(n->nif.test);
8079 break;
8080 case NFOR:
8081 funcstringsize += strlen(n->nfor.var) + 1;
8082 calcsize(n->nfor.body);
8083 calcsize(n->nfor.args);
8084 break;
8085 case NCASE:
8086 calcsize(n->ncase.cases);
8087 calcsize(n->ncase.expr);
8088 break;
8089 case NCLIST:
8090 calcsize(n->nclist.body);
8091 calcsize(n->nclist.pattern);
8092 calcsize(n->nclist.next);
8093 break;
8094 case NDEFUN:
8095 case NARG:
8096 sizenodelist(n->narg.backquote);
8097 funcstringsize += strlen(n->narg.text) + 1;
8098 calcsize(n->narg.next);
8099 break;
8100 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008101#if ENABLE_ASH_BASH_COMPAT
8102 case NTO2:
8103#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008104 case NCLOBBER:
8105 case NFROM:
8106 case NFROMTO:
8107 case NAPPEND:
8108 calcsize(n->nfile.fname);
8109 calcsize(n->nfile.next);
8110 break;
8111 case NTOFD:
8112 case NFROMFD:
8113 calcsize(n->ndup.vname);
8114 calcsize(n->ndup.next);
8115 break;
8116 case NHERE:
8117 case NXHERE:
8118 calcsize(n->nhere.doc);
8119 calcsize(n->nhere.next);
8120 break;
8121 case NNOT:
8122 calcsize(n->nnot.com);
8123 break;
8124 };
8125}
8126
8127static char *
8128nodeckstrdup(char *s)
8129{
8130 char *rtn = funcstring;
8131
8132 strcpy(funcstring, s);
8133 funcstring += strlen(s) + 1;
8134 return rtn;
8135}
8136
8137static union node *copynode(union node *);
8138
8139static struct nodelist *
8140copynodelist(struct nodelist *lp)
8141{
8142 struct nodelist *start;
8143 struct nodelist **lpp;
8144
8145 lpp = &start;
8146 while (lp) {
8147 *lpp = funcblock;
8148 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8149 (*lpp)->n = copynode(lp->n);
8150 lp = lp->next;
8151 lpp = &(*lpp)->next;
8152 }
8153 *lpp = NULL;
8154 return start;
8155}
8156
8157static union node *
8158copynode(union node *n)
8159{
8160 union node *new;
8161
8162 if (n == NULL)
8163 return NULL;
8164 new = funcblock;
8165 funcblock = (char *) funcblock + nodesize[n->type];
8166
8167 switch (n->type) {
8168 case NCMD:
8169 new->ncmd.redirect = copynode(n->ncmd.redirect);
8170 new->ncmd.args = copynode(n->ncmd.args);
8171 new->ncmd.assign = copynode(n->ncmd.assign);
8172 break;
8173 case NPIPE:
8174 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008175 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008176 break;
8177 case NREDIR:
8178 case NBACKGND:
8179 case NSUBSHELL:
8180 new->nredir.redirect = copynode(n->nredir.redirect);
8181 new->nredir.n = copynode(n->nredir.n);
8182 break;
8183 case NAND:
8184 case NOR:
8185 case NSEMI:
8186 case NWHILE:
8187 case NUNTIL:
8188 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8189 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8190 break;
8191 case NIF:
8192 new->nif.elsepart = copynode(n->nif.elsepart);
8193 new->nif.ifpart = copynode(n->nif.ifpart);
8194 new->nif.test = copynode(n->nif.test);
8195 break;
8196 case NFOR:
8197 new->nfor.var = nodeckstrdup(n->nfor.var);
8198 new->nfor.body = copynode(n->nfor.body);
8199 new->nfor.args = copynode(n->nfor.args);
8200 break;
8201 case NCASE:
8202 new->ncase.cases = copynode(n->ncase.cases);
8203 new->ncase.expr = copynode(n->ncase.expr);
8204 break;
8205 case NCLIST:
8206 new->nclist.body = copynode(n->nclist.body);
8207 new->nclist.pattern = copynode(n->nclist.pattern);
8208 new->nclist.next = copynode(n->nclist.next);
8209 break;
8210 case NDEFUN:
8211 case NARG:
8212 new->narg.backquote = copynodelist(n->narg.backquote);
8213 new->narg.text = nodeckstrdup(n->narg.text);
8214 new->narg.next = copynode(n->narg.next);
8215 break;
8216 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008217#if ENABLE_ASH_BASH_COMPAT
8218 case NTO2:
8219#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008220 case NCLOBBER:
8221 case NFROM:
8222 case NFROMTO:
8223 case NAPPEND:
8224 new->nfile.fname = copynode(n->nfile.fname);
8225 new->nfile.fd = n->nfile.fd;
8226 new->nfile.next = copynode(n->nfile.next);
8227 break;
8228 case NTOFD:
8229 case NFROMFD:
8230 new->ndup.vname = copynode(n->ndup.vname);
8231 new->ndup.dupfd = n->ndup.dupfd;
8232 new->ndup.fd = n->ndup.fd;
8233 new->ndup.next = copynode(n->ndup.next);
8234 break;
8235 case NHERE:
8236 case NXHERE:
8237 new->nhere.doc = copynode(n->nhere.doc);
8238 new->nhere.fd = n->nhere.fd;
8239 new->nhere.next = copynode(n->nhere.next);
8240 break;
8241 case NNOT:
8242 new->nnot.com = copynode(n->nnot.com);
8243 break;
8244 };
8245 new->type = n->type;
8246 return new;
8247}
8248
8249/*
8250 * Make a copy of a parse tree.
8251 */
8252static struct funcnode *
8253copyfunc(union node *n)
8254{
8255 struct funcnode *f;
8256 size_t blocksize;
8257
8258 funcblocksize = offsetof(struct funcnode, n);
8259 funcstringsize = 0;
8260 calcsize(n);
8261 blocksize = funcblocksize;
8262 f = ckmalloc(blocksize + funcstringsize);
8263 funcblock = (char *) f + offsetof(struct funcnode, n);
8264 funcstring = (char *) f + blocksize;
8265 copynode(n);
8266 f->count = 0;
8267 return f;
8268}
8269
8270/*
8271 * Define a shell function.
8272 */
8273static void
8274defun(char *name, union node *func)
8275{
8276 struct cmdentry entry;
8277
8278 INT_OFF;
8279 entry.cmdtype = CMDFUNCTION;
8280 entry.u.func = copyfunc(func);
8281 addcmdentry(name, &entry);
8282 INT_ON;
8283}
8284
Denis Vlasenko4b875702009-03-19 13:30:04 +00008285/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008286#define SKIPBREAK (1 << 0)
8287#define SKIPCONT (1 << 1)
8288#define SKIPFUNC (1 << 2)
8289#define SKIPFILE (1 << 3)
8290#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008291static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008292static int skipcount; /* number of levels to skip */
8293static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008294static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008295
Denis Vlasenko4b875702009-03-19 13:30:04 +00008296/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008297static int evalstring(char *s, int mask);
8298
Denis Vlasenko4b875702009-03-19 13:30:04 +00008299/* Called to execute a trap.
8300 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008301 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008302 *
8303 * Perhaps we should avoid entering new trap handlers
8304 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008305 */
8306static int
8307dotrap(void)
8308{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008309 uint8_t *g;
8310 int sig;
8311 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008312
8313 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008314 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008315 xbarrier();
8316
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008317 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008318 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8319 int want_exexit;
8320 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008321
Denis Vlasenko4b875702009-03-19 13:30:04 +00008322 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008323 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008324 t = trap[sig];
8325 /* non-trapped SIGINT is handled separately by raise_interrupt,
8326 * don't upset it by resetting gotsig[SIGINT-1] */
8327 if (sig == SIGINT && !t)
8328 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008329
8330 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008331 *g = 0;
8332 if (!t)
8333 continue;
8334 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008335 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008336 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008337 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008338 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008339 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008340 }
8341
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008342 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008343 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008344}
8345
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008346/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008347static void evalloop(union node *, int);
8348static void evalfor(union node *, int);
8349static void evalcase(union node *, int);
8350static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008351static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008352static void evalpipe(union node *, int);
8353static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008354static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008355static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008356
Eric Andersen62483552001-07-10 06:09:16 +00008357/*
Eric Andersenc470f442003-07-28 09:56:35 +00008358 * Evaluate a parse tree. The value is left in the global variable
8359 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008360 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008361static void
Eric Andersenc470f442003-07-28 09:56:35 +00008362evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008363{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008364 struct jmploc *volatile savehandler = exception_handler;
8365 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008366 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008367 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008368 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008369 int int_level;
8370
8371 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008372
Eric Andersenc470f442003-07-28 09:56:35 +00008373 if (n == NULL) {
8374 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008375 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008376 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008377 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008378
8379 exception_handler = &jmploc;
8380 {
8381 int err = setjmp(jmploc.loc);
8382 if (err) {
8383 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008384 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008385 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8386 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008387 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008388 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008389 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008390 TRACE(("exception %d in evaltree, propagating err=%d\n",
8391 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008392 exception_handler = savehandler;
8393 longjmp(exception_handler->loc, err);
8394 }
8395 }
8396
Eric Andersenc470f442003-07-28 09:56:35 +00008397 switch (n->type) {
8398 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008399#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008400 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008401 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008402 break;
8403#endif
8404 case NNOT:
8405 evaltree(n->nnot.com, EV_TESTED);
8406 status = !exitstatus;
8407 goto setstatus;
8408 case NREDIR:
8409 expredir(n->nredir.redirect);
8410 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8411 if (!status) {
8412 evaltree(n->nredir.n, flags & EV_TESTED);
8413 status = exitstatus;
8414 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008415 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008416 goto setstatus;
8417 case NCMD:
8418 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008419 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008420 if (eflag && !(flags & EV_TESTED))
8421 checkexit = ~0;
8422 goto calleval;
8423 case NFOR:
8424 evalfn = evalfor;
8425 goto calleval;
8426 case NWHILE:
8427 case NUNTIL:
8428 evalfn = evalloop;
8429 goto calleval;
8430 case NSUBSHELL:
8431 case NBACKGND:
8432 evalfn = evalsubshell;
8433 goto calleval;
8434 case NPIPE:
8435 evalfn = evalpipe;
8436 goto checkexit;
8437 case NCASE:
8438 evalfn = evalcase;
8439 goto calleval;
8440 case NAND:
8441 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008442 case NSEMI: {
8443
Eric Andersenc470f442003-07-28 09:56:35 +00008444#if NAND + 1 != NOR
8445#error NAND + 1 != NOR
8446#endif
8447#if NOR + 1 != NSEMI
8448#error NOR + 1 != NSEMI
8449#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008450 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008451 evaltree(
8452 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008453 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008454 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008455 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008456 break;
8457 if (!evalskip) {
8458 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008459 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008460 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008461 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008462 evalfn(n, flags);
8463 break;
8464 }
8465 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008466 }
Eric Andersenc470f442003-07-28 09:56:35 +00008467 case NIF:
8468 evaltree(n->nif.test, EV_TESTED);
8469 if (evalskip)
8470 break;
8471 if (exitstatus == 0) {
8472 n = n->nif.ifpart;
8473 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008474 }
8475 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008476 n = n->nif.elsepart;
8477 goto evaln;
8478 }
8479 goto success;
8480 case NDEFUN:
8481 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008482 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008483 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008484 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008485 exitstatus = status;
8486 break;
8487 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008488
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008489 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008490 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008491
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008492 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008493 /* Order of checks below is important:
8494 * signal handlers trigger before exit caused by "set -e".
8495 */
8496 if (pending_sig && dotrap())
8497 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008498 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008499 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008500
8501 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008502 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008503 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008504 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008505
8506 RESTORE_INT(int_level);
8507 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008508}
8509
Eric Andersenc470f442003-07-28 09:56:35 +00008510#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8511static
8512#endif
8513void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8514
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008515static void
Eric Andersenc470f442003-07-28 09:56:35 +00008516evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008517{
8518 int status;
8519
8520 loopnest++;
8521 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008522 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008523 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008524 int i;
8525
Eric Andersencb57d552001-06-28 07:25:16 +00008526 evaltree(n->nbinary.ch1, EV_TESTED);
8527 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008528 skipping:
8529 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008530 evalskip = 0;
8531 continue;
8532 }
8533 if (evalskip == SKIPBREAK && --skipcount <= 0)
8534 evalskip = 0;
8535 break;
8536 }
Eric Andersenc470f442003-07-28 09:56:35 +00008537 i = exitstatus;
8538 if (n->type != NWHILE)
8539 i = !i;
8540 if (i != 0)
8541 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008542 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008543 status = exitstatus;
8544 if (evalskip)
8545 goto skipping;
8546 }
8547 loopnest--;
8548 exitstatus = status;
8549}
8550
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008551static void
Eric Andersenc470f442003-07-28 09:56:35 +00008552evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008553{
8554 struct arglist arglist;
8555 union node *argp;
8556 struct strlist *sp;
8557 struct stackmark smark;
8558
8559 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008560 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008561 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008562 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008563 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008564 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008565 if (evalskip)
8566 goto out;
8567 }
8568 *arglist.lastp = NULL;
8569
8570 exitstatus = 0;
8571 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008572 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008573 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008574 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008575 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008576 if (evalskip) {
8577 if (evalskip == SKIPCONT && --skipcount <= 0) {
8578 evalskip = 0;
8579 continue;
8580 }
8581 if (evalskip == SKIPBREAK && --skipcount <= 0)
8582 evalskip = 0;
8583 break;
8584 }
8585 }
8586 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008587 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008588 popstackmark(&smark);
8589}
8590
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008591static void
Eric Andersenc470f442003-07-28 09:56:35 +00008592evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008593{
8594 union node *cp;
8595 union node *patp;
8596 struct arglist arglist;
8597 struct stackmark smark;
8598
8599 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008600 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008601 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008602 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008603 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008604 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8605 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008606 if (casematch(patp, arglist.list->text)) {
8607 if (evalskip == 0) {
8608 evaltree(cp->nclist.body, flags);
8609 }
8610 goto out;
8611 }
8612 }
8613 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008614 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008615 popstackmark(&smark);
8616}
8617
Eric Andersenc470f442003-07-28 09:56:35 +00008618/*
8619 * Kick off a subshell to evaluate a tree.
8620 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008621static void
Eric Andersenc470f442003-07-28 09:56:35 +00008622evalsubshell(union node *n, int flags)
8623{
8624 struct job *jp;
8625 int backgnd = (n->type == NBACKGND);
8626 int status;
8627
8628 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008629 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008630 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008631 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008632 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008633 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008634 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008635 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008636 flags |= EV_EXIT;
8637 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008638 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008639 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008640 redirect(n->nredir.redirect, 0);
8641 evaltreenr(n->nredir.n, flags);
8642 /* never returns */
8643 }
8644 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008645 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008646 status = waitforjob(jp);
8647 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008648 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008649}
8650
Eric Andersenc470f442003-07-28 09:56:35 +00008651/*
8652 * Compute the names of the files in a redirection list.
8653 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008654static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008655static void
8656expredir(union node *n)
8657{
8658 union node *redir;
8659
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008660 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008661 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008662
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008663 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008664 fn.lastp = &fn.list;
8665 switch (redir->type) {
8666 case NFROMTO:
8667 case NFROM:
8668 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008669#if ENABLE_ASH_BASH_COMPAT
8670 case NTO2:
8671#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008672 case NCLOBBER:
8673 case NAPPEND:
8674 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008675 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denis Vlasenko559691a2008-10-05 18:39:31 +00008676#if ENABLE_ASH_BASH_COMPAT
8677 store_expfname:
8678#endif
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01008679 if (redir->nfile.expfname)
8680 stunalloc(redir->nfile.expfname);
Eric Andersenc470f442003-07-28 09:56:35 +00008681 redir->nfile.expfname = fn.list->text;
8682 break;
8683 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008684 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008685 if (redir->ndup.vname) {
8686 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008687 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008688 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008689#if ENABLE_ASH_BASH_COMPAT
8690//FIXME: we used expandarg with different args!
8691 if (!isdigit_str9(fn.list->text)) {
8692 /* >&file, not >&fd */
8693 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8694 ash_msg_and_raise_error("redir error");
8695 redir->type = NTO2;
8696 goto store_expfname;
8697 }
8698#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008699 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008700 }
8701 break;
8702 }
8703 }
8704}
8705
Eric Andersencb57d552001-06-28 07:25:16 +00008706/*
Eric Andersencb57d552001-06-28 07:25:16 +00008707 * Evaluate a pipeline. All the processes in the pipeline are children
8708 * of the process creating the pipeline. (This differs from some versions
8709 * of the shell, which make the last process in a pipeline the parent
8710 * of all the rest.)
8711 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008712static void
Eric Andersenc470f442003-07-28 09:56:35 +00008713evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008714{
8715 struct job *jp;
8716 struct nodelist *lp;
8717 int pipelen;
8718 int prevfd;
8719 int pip[2];
8720
Eric Andersenc470f442003-07-28 09:56:35 +00008721 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008722 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008723 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008724 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008725 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008726 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008727 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008728 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008729 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008730 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008731 pip[1] = -1;
8732 if (lp->next) {
8733 if (pipe(pip) < 0) {
8734 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008735 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008736 }
8737 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008738 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008739 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008740 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008741 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008742 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008743 if (prevfd > 0) {
8744 dup2(prevfd, 0);
8745 close(prevfd);
8746 }
8747 if (pip[1] > 1) {
8748 dup2(pip[1], 1);
8749 close(pip[1]);
8750 }
Eric Andersenc470f442003-07-28 09:56:35 +00008751 evaltreenr(lp->n, flags);
8752 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008753 }
8754 if (prevfd >= 0)
8755 close(prevfd);
8756 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008757 /* Don't want to trigger debugging */
8758 if (pip[1] != -1)
8759 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008760 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008761 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008762 exitstatus = waitforjob(jp);
8763 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008764 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008765 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008766}
8767
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008768/*
8769 * Controls whether the shell is interactive or not.
8770 */
8771static void
8772setinteractive(int on)
8773{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008774 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008775
8776 if (++on == is_interactive)
8777 return;
8778 is_interactive = on;
8779 setsignal(SIGINT);
8780 setsignal(SIGQUIT);
8781 setsignal(SIGTERM);
8782#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8783 if (is_interactive > 1) {
8784 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008785 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008786
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008787 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008788 /* note: ash and hush share this string */
8789 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008790 "Enter 'help' for a list of built-in commands."
8791 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008792 bb_banner,
8793 "built-in shell (ash)"
8794 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008795 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008796 }
8797 }
8798#endif
8799}
8800
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008801static void
8802optschanged(void)
8803{
8804#if DEBUG
8805 opentrace();
8806#endif
8807 setinteractive(iflag);
8808 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008809#if ENABLE_FEATURE_EDITING_VI
8810 if (viflag)
8811 line_input_state->flags |= VI_MODE;
8812 else
8813 line_input_state->flags &= ~VI_MODE;
8814#else
8815 viflag = 0; /* forcibly keep the option off */
8816#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008817}
8818
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819static struct localvar *localvars;
8820
8821/*
8822 * Called after a function returns.
8823 * Interrupts must be off.
8824 */
8825static void
8826poplocalvars(void)
8827{
8828 struct localvar *lvp;
8829 struct var *vp;
8830
8831 while ((lvp = localvars) != NULL) {
8832 localvars = lvp->next;
8833 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008834 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008835 if (vp == NULL) { /* $- saved */
8836 memcpy(optlist, lvp->text, sizeof(optlist));
8837 free((char*)lvp->text);
8838 optschanged();
8839 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008840 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008841 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008842 if (vp->var_func)
8843 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008844 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008845 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008846 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008847 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008848 }
8849 free(lvp);
8850 }
8851}
8852
8853static int
8854evalfun(struct funcnode *func, int argc, char **argv, int flags)
8855{
8856 volatile struct shparam saveparam;
8857 struct localvar *volatile savelocalvars;
8858 struct jmploc *volatile savehandler;
8859 struct jmploc jmploc;
8860 int e;
8861
8862 saveparam = shellparam;
8863 savelocalvars = localvars;
8864 e = setjmp(jmploc.loc);
8865 if (e) {
8866 goto funcdone;
8867 }
8868 INT_OFF;
8869 savehandler = exception_handler;
8870 exception_handler = &jmploc;
8871 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008872 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008873 func->count++;
8874 funcnest++;
8875 INT_ON;
8876 shellparam.nparam = argc - 1;
8877 shellparam.p = argv + 1;
8878#if ENABLE_ASH_GETOPTS
8879 shellparam.optind = 1;
8880 shellparam.optoff = -1;
8881#endif
8882 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008883 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008884 INT_OFF;
8885 funcnest--;
8886 freefunc(func);
8887 poplocalvars();
8888 localvars = savelocalvars;
8889 freeparam(&shellparam);
8890 shellparam = saveparam;
8891 exception_handler = savehandler;
8892 INT_ON;
8893 evalskip &= ~SKIPFUNC;
8894 return e;
8895}
8896
Denis Vlasenko131ae172007-02-18 13:00:19 +00008897#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008898static char **
8899parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008900{
8901 char *cp, c;
8902
8903 for (;;) {
8904 cp = *++argv;
8905 if (!cp)
8906 return 0;
8907 if (*cp++ != '-')
8908 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008909 c = *cp++;
8910 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008911 break;
8912 if (c == '-' && !*cp) {
8913 argv++;
8914 break;
8915 }
8916 do {
8917 switch (c) {
8918 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008919 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008920 break;
8921 default:
8922 /* run 'typecmd' for other options */
8923 return 0;
8924 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008925 c = *cp++;
8926 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008927 }
8928 return argv;
8929}
8930#endif
8931
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008932/*
8933 * Make a variable a local variable. When a variable is made local, it's
8934 * value and flags are saved in a localvar structure. The saved values
8935 * will be restored when the shell function returns. We handle the name
8936 * "-" as a special case.
8937 */
8938static void
8939mklocal(char *name)
8940{
8941 struct localvar *lvp;
8942 struct var **vpp;
8943 struct var *vp;
8944
8945 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008946 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008947 if (LONE_DASH(name)) {
8948 char *p;
8949 p = ckmalloc(sizeof(optlist));
8950 lvp->text = memcpy(p, optlist, sizeof(optlist));
8951 vp = NULL;
8952 } else {
8953 char *eq;
8954
8955 vpp = hashvar(name);
8956 vp = *findvar(vpp, name);
8957 eq = strchr(name, '=');
8958 if (vp == NULL) {
8959 if (eq)
8960 setvareq(name, VSTRFIXED);
8961 else
8962 setvar(name, NULL, VSTRFIXED);
8963 vp = *vpp; /* the new variable */
8964 lvp->flags = VUNSET;
8965 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008966 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008967 lvp->flags = vp->flags;
8968 vp->flags |= VSTRFIXED|VTEXTFIXED;
8969 if (eq)
8970 setvareq(name, 0);
8971 }
8972 }
8973 lvp->vp = vp;
8974 lvp->next = localvars;
8975 localvars = lvp;
8976 INT_ON;
8977}
8978
8979/*
8980 * The "local" command.
8981 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008982static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008983localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008984{
8985 char *name;
8986
8987 argv = argptr;
8988 while ((name = *argv++) != NULL) {
8989 mklocal(name);
8990 }
8991 return 0;
8992}
8993
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008994static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008995falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008996{
8997 return 1;
8998}
8999
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009000static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009001truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009002{
9003 return 0;
9004}
9005
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009006static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009007execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009008{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009009 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009010 iflag = 0; /* exit on error */
9011 mflag = 0;
9012 optschanged();
9013 shellexec(argv + 1, pathval(), 0);
9014 }
9015 return 0;
9016}
9017
9018/*
9019 * The return command.
9020 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009021static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009022returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009023{
9024 /*
9025 * If called outside a function, do what ksh does;
9026 * skip the rest of the file.
9027 */
9028 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9029 return argv[1] ? number(argv[1]) : exitstatus;
9030}
9031
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009032/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009033static int breakcmd(int, char **) FAST_FUNC;
9034static int dotcmd(int, char **) FAST_FUNC;
9035static int evalcmd(int, char **) FAST_FUNC;
9036static int exitcmd(int, char **) FAST_FUNC;
9037static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009038#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009039static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009040#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009041#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009042static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009043#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009044#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009045static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009046#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009047static int readcmd(int, char **) FAST_FUNC;
9048static int setcmd(int, char **) FAST_FUNC;
9049static int shiftcmd(int, char **) FAST_FUNC;
9050static int timescmd(int, char **) FAST_FUNC;
9051static int trapcmd(int, char **) FAST_FUNC;
9052static int umaskcmd(int, char **) FAST_FUNC;
9053static int unsetcmd(int, char **) FAST_FUNC;
9054static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009055
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009056#define BUILTIN_NOSPEC "0"
9057#define BUILTIN_SPECIAL "1"
9058#define BUILTIN_REGULAR "2"
9059#define BUILTIN_SPEC_REG "3"
9060#define BUILTIN_ASSIGN "4"
9061#define BUILTIN_SPEC_ASSG "5"
9062#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009063#define BUILTIN_SPEC_REG_ASSG "7"
9064
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009065/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009066#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009067static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009068#endif
9069#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009070static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009071#endif
9072#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009073static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009074#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009075
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009076/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009077static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009078 { BUILTIN_SPEC_REG "." , dotcmd },
9079 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009080#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009081 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009082#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009083 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009084#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009085#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009086#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009087 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009088#endif
9089#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009090 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009091#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009092 { BUILTIN_SPEC_REG "break" , breakcmd },
9093 { BUILTIN_REGULAR "cd" , cdcmd },
9094 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009095#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009096 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009097#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009098 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009099#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009100 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009101#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009102 { BUILTIN_SPEC_REG "eval" , evalcmd },
9103 { BUILTIN_SPEC_REG "exec" , execcmd },
9104 { BUILTIN_SPEC_REG "exit" , exitcmd },
9105 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9106 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009107#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009108 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009109#endif
9110#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009111 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009112#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009113 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009114#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009115 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009116#endif
9117#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009118 { BUILTIN_REGULAR "jobs" , jobscmd },
9119 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009120#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009121#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009122 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009123#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009124 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009125#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009126 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009127#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009128 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9129 { BUILTIN_REGULAR "read" , readcmd },
9130 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9131 { BUILTIN_SPEC_REG "return" , returncmd },
9132 { BUILTIN_SPEC_REG "set" , setcmd },
9133 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009134#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009135 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009136#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009137#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009138 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009139#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009140 { BUILTIN_SPEC_REG "times" , timescmd },
9141 { BUILTIN_SPEC_REG "trap" , trapcmd },
9142 { BUILTIN_REGULAR "true" , truecmd },
9143 { BUILTIN_NOSPEC "type" , typecmd },
9144 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9145 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009146#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009147 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009148#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009149 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9150 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009151};
9152
Denis Vlasenko80591b02008-03-25 07:49:43 +00009153/* Should match the above table! */
9154#define COMMANDCMD (builtintab + \
9155 2 + \
9156 1 * ENABLE_ASH_BUILTIN_TEST + \
9157 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9158 1 * ENABLE_ASH_ALIAS + \
9159 1 * ENABLE_ASH_JOB_CONTROL + \
9160 3)
9161#define EXECCMD (builtintab + \
9162 2 + \
9163 1 * ENABLE_ASH_BUILTIN_TEST + \
9164 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9165 1 * ENABLE_ASH_ALIAS + \
9166 1 * ENABLE_ASH_JOB_CONTROL + \
9167 3 + \
9168 1 * ENABLE_ASH_CMDCMD + \
9169 1 + \
9170 ENABLE_ASH_BUILTIN_ECHO + \
9171 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009172
9173/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009174 * Search the table of builtin commands.
9175 */
9176static struct builtincmd *
9177find_builtin(const char *name)
9178{
9179 struct builtincmd *bp;
9180
9181 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009182 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009183 pstrcmp
9184 );
9185 return bp;
9186}
9187
9188/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009189 * Execute a simple command.
9190 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009191static int
9192isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009193{
9194 const char *q = endofname(p);
9195 if (p == q)
9196 return 0;
9197 return *q == '=';
9198}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009199static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009200bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009201{
9202 /* Preserve exitstatus of a previous possible redirection
9203 * as POSIX mandates */
9204 return back_exitstatus;
9205}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009206static void
Eric Andersenc470f442003-07-28 09:56:35 +00009207evalcommand(union node *cmd, int flags)
9208{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009209 static const struct builtincmd null_bltin = {
9210 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009211 };
Eric Andersenc470f442003-07-28 09:56:35 +00009212 struct stackmark smark;
9213 union node *argp;
9214 struct arglist arglist;
9215 struct arglist varlist;
9216 char **argv;
9217 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009218 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009219 struct cmdentry cmdentry;
9220 struct job *jp;
9221 char *lastarg;
9222 const char *path;
9223 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009224 int status;
9225 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009226 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009227 smallint cmd_is_exec;
9228 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009229
9230 /* First expand the arguments. */
9231 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9232 setstackmark(&smark);
9233 back_exitstatus = 0;
9234
9235 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009236 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009237 varlist.lastp = &varlist.list;
9238 *varlist.lastp = NULL;
9239 arglist.lastp = &arglist.list;
9240 *arglist.lastp = NULL;
9241
9242 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009243 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009244 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9245 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9246 }
9247
Eric Andersenc470f442003-07-28 09:56:35 +00009248 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9249 struct strlist **spp;
9250
9251 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009252 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009253 expandarg(argp, &arglist, EXP_VARTILDE);
9254 else
9255 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9256
Eric Andersenc470f442003-07-28 09:56:35 +00009257 for (sp = *spp; sp; sp = sp->next)
9258 argc++;
9259 }
9260
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009261 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009262 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009263 TRACE(("evalcommand arg: %s\n", sp->text));
9264 *nargv++ = sp->text;
9265 }
9266 *nargv = NULL;
9267
9268 lastarg = NULL;
9269 if (iflag && funcnest == 0 && argc > 0)
9270 lastarg = nargv[-1];
9271
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009272 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009273 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009274 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009275
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009276 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009277 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9278 struct strlist **spp;
9279 char *p;
9280
9281 spp = varlist.lastp;
9282 expandarg(argp, &varlist, EXP_VARTILDE);
9283
9284 /*
9285 * Modify the command lookup path, if a PATH= assignment
9286 * is present
9287 */
9288 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009289 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009290 path = p;
9291 }
9292
9293 /* Print the command if xflag is set. */
9294 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009295 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009296 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009297
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009298 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009299 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009300 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009301 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009302 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009303 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009304 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009305 }
9306 sp = arglist.list;
9307 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009308 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009309 }
9310
9311 cmd_is_exec = 0;
9312 spclbltin = -1;
9313
9314 /* Now locate the command. */
9315 if (argc) {
Eric Andersenc470f442003-07-28 09:56:35 +00009316 int cmd_flag = DO_ERR;
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02009317#if ENABLE_ASH_CMDCMD
9318 const char *oldpath = path + 5;
9319#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009320 path += 5;
Eric Andersenc470f442003-07-28 09:56:35 +00009321 for (;;) {
9322 find_command(argv[0], &cmdentry, cmd_flag, path);
9323 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009324 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009325 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009326 goto bail;
9327 }
9328
9329 /* implement bltin and command here */
9330 if (cmdentry.cmdtype != CMDBUILTIN)
9331 break;
9332 if (spclbltin < 0)
9333 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9334 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009335 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009336#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009337 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009338 path = oldpath;
9339 nargv = parse_command_args(argv, &path);
9340 if (!nargv)
9341 break;
9342 argc -= nargv - argv;
9343 argv = nargv;
9344 cmd_flag |= DO_NOFUNC;
9345 } else
9346#endif
9347 break;
9348 }
9349 }
9350
9351 if (status) {
9352 /* We have a redirection error. */
9353 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009354 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009355 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009356 exitstatus = status;
9357 goto out;
9358 }
9359
9360 /* Execute the command. */
9361 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009362 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009363
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009364#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009365/* (1) BUG: if variables are set, we need to fork, or save/restore them
9366 * around run_nofork_applet() call.
9367 * (2) Should this check also be done in forkshell()?
9368 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9369 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009370 /* find_command() encodes applet_no as (-2 - applet_no) */
9371 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009372 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009373 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009374 /* run <applet>_main() */
9375 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009376 break;
9377 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009378#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009379 /* Can we avoid forking off? For example, very last command
9380 * in a script or a subshell does not need forking,
9381 * we can just exec it.
9382 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009383 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009384 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009385 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009386 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009387 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009388 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009389 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009390 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009391 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009392 break;
9393 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009394 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009395 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009396 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009397 }
9398 listsetvar(varlist.list, VEXPORT|VSTACK);
9399 shellexec(argv, path, cmdentry.u.index);
9400 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009401 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009402 case CMDBUILTIN:
9403 cmdenviron = varlist.list;
9404 if (cmdenviron) {
9405 struct strlist *list = cmdenviron;
9406 int i = VNOSET;
9407 if (spclbltin > 0 || argc == 0) {
9408 i = 0;
9409 if (cmd_is_exec && argc > 1)
9410 i = VEXPORT;
9411 }
9412 listsetvar(list, i);
9413 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009414 /* Tight loop with builtins only:
9415 * "while kill -0 $child; do true; done"
9416 * will never exit even if $child died, unless we do this
9417 * to reap the zombie and make kill detect that it's gone: */
9418 dowait(DOWAIT_NONBLOCK, NULL);
9419
Eric Andersenc470f442003-07-28 09:56:35 +00009420 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9421 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009422 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009423 if (i == EXEXIT)
9424 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009425 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009426 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009427 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009428 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009429 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009430 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009431 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009432 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009433 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009434 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009435 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009436 }
9437 break;
9438
9439 case CMDFUNCTION:
9440 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009441 /* See above for the rationale */
9442 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009443 if (evalfun(cmdentry.u.func, argc, argv, flags))
9444 goto raise;
9445 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009446
9447 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009448
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009449 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009450 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009451 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009452 /* dsl: I think this is intended to be used to support
9453 * '_' in 'vi' command mode during line editing...
9454 * However I implemented that within libedit itself.
9455 */
9456 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009457 }
Eric Andersenc470f442003-07-28 09:56:35 +00009458 popstackmark(&smark);
9459}
9460
9461static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009462evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9463{
Eric Andersenc470f442003-07-28 09:56:35 +00009464 char *volatile savecmdname;
9465 struct jmploc *volatile savehandler;
9466 struct jmploc jmploc;
9467 int i;
9468
9469 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009470 i = setjmp(jmploc.loc);
9471 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009472 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009473 savehandler = exception_handler;
9474 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009475 commandname = argv[0];
9476 argptr = argv + 1;
9477 optptr = NULL; /* initialize nextopt */
9478 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009479 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009480 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009481 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009482 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009483 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009484 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009485
9486 return i;
9487}
9488
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009489static int
9490goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009491{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009492 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009493}
9494
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009495
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009496/*
9497 * Search for a command. This is called before we fork so that the
9498 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009499 * the child. The check for "goodname" is an overly conservative
9500 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009501 */
Eric Andersenc470f442003-07-28 09:56:35 +00009502static void
9503prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009504{
9505 struct cmdentry entry;
9506
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009507 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9508 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009509}
9510
Eric Andersencb57d552001-06-28 07:25:16 +00009511
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009512/* ============ Builtin commands
9513 *
9514 * Builtin commands whose functions are closely tied to evaluation
9515 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009516 */
9517
9518/*
Eric Andersencb57d552001-06-28 07:25:16 +00009519 * Handle break and continue commands. Break, continue, and return are
9520 * all handled by setting the evalskip flag. The evaluation routines
9521 * above all check this flag, and if it is set they start skipping
9522 * commands rather than executing them. The variable skipcount is
9523 * the number of loops to break/continue, or the number of function
9524 * levels to return. (The latter is always 1.) It should probably
9525 * be an error to break out of more loops than exist, but it isn't
9526 * in the standard shell so we don't make it one here.
9527 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009528static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009529breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009530{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009531 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009532
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009533 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009534 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009535 if (n > loopnest)
9536 n = loopnest;
9537 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009538 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009539 skipcount = n;
9540 }
9541 return 0;
9542}
9543
Eric Andersenc470f442003-07-28 09:56:35 +00009544
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009545/* ============ input.c
9546 *
Eric Andersen90898442003-08-06 11:20:52 +00009547 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009548 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009549
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009550enum {
9551 INPUT_PUSH_FILE = 1,
9552 INPUT_NOFILE_OK = 2,
9553};
Eric Andersencb57d552001-06-28 07:25:16 +00009554
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009555static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009556/* values of checkkwd variable */
9557#define CHKALIAS 0x1
9558#define CHKKWD 0x2
9559#define CHKNL 0x4
9560
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009561/*
9562 * Push a string back onto the input at this current parsefile level.
9563 * We handle aliases this way.
9564 */
9565#if !ENABLE_ASH_ALIAS
9566#define pushstring(s, ap) pushstring(s)
9567#endif
9568static void
9569pushstring(char *s, struct alias *ap)
9570{
9571 struct strpush *sp;
9572 int len;
9573
9574 len = strlen(s);
9575 INT_OFF;
9576 if (g_parsefile->strpush) {
9577 sp = ckzalloc(sizeof(*sp));
9578 sp->prev = g_parsefile->strpush;
9579 } else {
9580 sp = &(g_parsefile->basestrpush);
9581 }
9582 g_parsefile->strpush = sp;
9583 sp->prev_string = g_parsefile->next_to_pgetc;
9584 sp->prev_left_in_line = g_parsefile->left_in_line;
9585#if ENABLE_ASH_ALIAS
9586 sp->ap = ap;
9587 if (ap) {
9588 ap->flag |= ALIASINUSE;
9589 sp->string = s;
9590 }
9591#endif
9592 g_parsefile->next_to_pgetc = s;
9593 g_parsefile->left_in_line = len;
9594 INT_ON;
9595}
9596
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009597static void
9598popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009599{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009600 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009601
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009602 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009603#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009604 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009605 if (g_parsefile->next_to_pgetc[-1] == ' '
9606 || g_parsefile->next_to_pgetc[-1] == '\t'
9607 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009608 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009609 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009610 if (sp->string != sp->ap->val) {
9611 free(sp->string);
9612 }
9613 sp->ap->flag &= ~ALIASINUSE;
9614 if (sp->ap->flag & ALIASDEAD) {
9615 unalias(sp->ap->name);
9616 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009617 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009618#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009619 g_parsefile->next_to_pgetc = sp->prev_string;
9620 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009621 g_parsefile->strpush = sp->prev;
9622 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009623 free(sp);
9624 INT_ON;
9625}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009626
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009627//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9628//it peeks whether it is &>, and then pushes back both chars.
9629//This function needs to save last *next_to_pgetc to buf[0]
9630//to make two pungetc() reliable. Currently,
9631// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009632static int
9633preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009634{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009635 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009636 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009637
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009638 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009639#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009640 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009641 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009642 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009643 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009644 int timeout = -1;
9645# if ENABLE_ASH_IDLE_TIMEOUT
9646 if (iflag) {
9647 const char *tmout_var = lookupvar("TMOUT");
9648 if (tmout_var) {
9649 timeout = atoi(tmout_var) * 1000;
9650 if (timeout <= 0)
9651 timeout = -1;
9652 }
9653 }
9654# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009655# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009656 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009657# endif
Denys Vlasenko20704f02011-03-23 17:59:27 +01009658 /* Unicode support should be activated even if LANG is set
9659 * _during_ shell execution, not only if it was set when
9660 * shell was started. Therefore, re-check LANG every time:
9661 */
9662 reinit_unicode(lookupvar("LANG"));
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009663 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009664 if (nr == 0) {
9665 /* Ctrl+C pressed */
9666 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009667 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009668 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009669 raise(SIGINT);
9670 return 1;
9671 }
Eric Andersenc470f442003-07-28 09:56:35 +00009672 goto retry;
9673 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009674 if (nr < 0) {
9675 if (errno == 0) {
9676 /* Ctrl+D pressed */
9677 nr = 0;
9678 }
9679# if ENABLE_ASH_IDLE_TIMEOUT
9680 else if (errno == EAGAIN && timeout > 0) {
9681 printf("\007timed out waiting for input: auto-logout\n");
9682 exitshell();
9683 }
9684# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009685 }
Eric Andersencb57d552001-06-28 07:25:16 +00009686 }
9687#else
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009688 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009689#endif
9690
Denys Vlasenko80c5b682011-05-08 21:21:10 +02009691#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009692 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009693 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009694 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009695 if (flags >= 0 && (flags & O_NONBLOCK)) {
9696 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009697 if (fcntl(0, F_SETFL, flags) >= 0) {
9698 out2str("sh: turning off NDELAY mode\n");
9699 goto retry;
9700 }
9701 }
9702 }
9703 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009704#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009705 return nr;
9706}
9707
9708/*
9709 * Refill the input buffer and return the next input character:
9710 *
9711 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9713 * or we are reading from a string so we can't refill the buffer,
9714 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009715 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009716 * 4) Process input up to the next newline, deleting nul characters.
9717 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009718//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9719#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009720static int
Eric Andersenc470f442003-07-28 09:56:35 +00009721preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009722{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009723 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009724 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009725
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009726 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009727#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009728 if (g_parsefile->left_in_line == -1
9729 && g_parsefile->strpush->ap
9730 && g_parsefile->next_to_pgetc[-1] != ' '
9731 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009732 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009733 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009734 return PEOA;
9735 }
Eric Andersen2870d962001-07-02 17:27:21 +00009736#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009737 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009738 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009739 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9740 g_parsefile->left_in_line,
9741 g_parsefile->next_to_pgetc,
9742 g_parsefile->next_to_pgetc);
9743 if (--g_parsefile->left_in_line >= 0)
9744 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009745 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009746 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009747 * "pgetc" needs refilling.
9748 */
9749
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009750 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009751 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009752 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009753 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009754 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009755 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009756 /* even in failure keep left_in_line and next_to_pgetc
9757 * in lock step, for correct multi-layer pungetc.
9758 * left_in_line was decremented before preadbuffer(),
9759 * must inc next_to_pgetc: */
9760 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009761 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009762 }
Eric Andersencb57d552001-06-28 07:25:16 +00009763
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009764 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009765 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009766 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009767 again:
9768 more = preadfd();
9769 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009770 /* don't try reading again */
9771 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009772 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009773 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009774 return PEOF;
9775 }
9776 }
9777
Denis Vlasenko727752d2008-11-28 03:41:47 +00009778 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009779 * Set g_parsefile->left_in_line
9780 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009781 * NUL chars are deleted.
9782 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009783 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009784 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009785 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009786
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009787 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009788
Denis Vlasenko727752d2008-11-28 03:41:47 +00009789 c = *q;
9790 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009791 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009792 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009793 q++;
9794 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009795 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009796 break;
9797 }
Eric Andersencb57d552001-06-28 07:25:16 +00009798 }
9799
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009800 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009801 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9802 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009803 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009804 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009805 }
9806 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009807 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009808
Eric Andersencb57d552001-06-28 07:25:16 +00009809 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009810 char save = *q;
9811 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009812 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009813 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009814 }
9815
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009816 pgetc_debug("preadbuffer at %d:%p'%s'",
9817 g_parsefile->left_in_line,
9818 g_parsefile->next_to_pgetc,
9819 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009820 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009821}
9822
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009823#define pgetc_as_macro() \
9824 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009825 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009826 : preadbuffer() \
9827 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009828
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009829static int
9830pgetc(void)
9831{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009832 pgetc_debug("pgetc_fast at %d:%p'%s'",
9833 g_parsefile->left_in_line,
9834 g_parsefile->next_to_pgetc,
9835 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009836 return pgetc_as_macro();
9837}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009838
9839#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009840# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009841#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009842# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009843#endif
9844
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009845#if ENABLE_ASH_ALIAS
9846static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009847pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009848{
9849 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009850 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009851 pgetc_debug("pgetc_fast at %d:%p'%s'",
9852 g_parsefile->left_in_line,
9853 g_parsefile->next_to_pgetc,
9854 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009855 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009856 } while (c == PEOA);
9857 return c;
9858}
9859#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009860# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009861#endif
9862
9863/*
9864 * Read a line from the script.
9865 */
9866static char *
9867pfgets(char *line, int len)
9868{
9869 char *p = line;
9870 int nleft = len;
9871 int c;
9872
9873 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009874 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009875 if (c == PEOF) {
9876 if (p == line)
9877 return NULL;
9878 break;
9879 }
9880 *p++ = c;
9881 if (c == '\n')
9882 break;
9883 }
9884 *p = '\0';
9885 return line;
9886}
9887
Eric Andersenc470f442003-07-28 09:56:35 +00009888/*
9889 * Undo the last call to pgetc. Only one character may be pushed back.
9890 * PEOF may be pushed back.
9891 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009892static void
Eric Andersenc470f442003-07-28 09:56:35 +00009893pungetc(void)
9894{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009895 g_parsefile->left_in_line++;
9896 g_parsefile->next_to_pgetc--;
9897 pgetc_debug("pushed back to %d:%p'%s'",
9898 g_parsefile->left_in_line,
9899 g_parsefile->next_to_pgetc,
9900 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009901}
9902
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009903/*
9904 * To handle the "." command, a stack of input files is used. Pushfile
9905 * adds a new entry to the stack and popfile restores the previous level.
9906 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009907static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009908pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009909{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009910 struct parsefile *pf;
9911
Denis Vlasenko597906c2008-02-20 16:38:54 +00009912 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009913 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009914 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009915 /*pf->strpush = NULL; - ckzalloc did it */
9916 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009917 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009918}
9919
9920static void
9921popfile(void)
9922{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009923 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009924
Denis Vlasenkob012b102007-02-19 22:43:01 +00009925 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009926 if (pf->pf_fd >= 0)
9927 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009928 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009929 while (pf->strpush)
9930 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009931 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009932 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009933 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009934}
9935
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009936/*
9937 * Return to top level.
9938 */
9939static void
9940popallfiles(void)
9941{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009942 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009943 popfile();
9944}
9945
9946/*
9947 * Close the file(s) that the shell is reading commands from. Called
9948 * after a fork is done.
9949 */
9950static void
9951closescript(void)
9952{
9953 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009954 if (g_parsefile->pf_fd > 0) {
9955 close(g_parsefile->pf_fd);
9956 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009957 }
9958}
9959
9960/*
9961 * Like setinputfile, but takes an open file descriptor. Call this with
9962 * interrupts off.
9963 */
9964static void
9965setinputfd(int fd, int push)
9966{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009967 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009968 if (push) {
9969 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009970 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009971 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009972 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009973 if (g_parsefile->buf == NULL)
9974 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009975 g_parsefile->left_in_buffer = 0;
9976 g_parsefile->left_in_line = 0;
9977 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009978}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009979
Eric Andersenc470f442003-07-28 09:56:35 +00009980/*
9981 * Set the input to take input from a file. If push is set, push the
9982 * old input onto the stack first.
9983 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009984static int
9985setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009986{
9987 int fd;
9988 int fd2;
9989
Denis Vlasenkob012b102007-02-19 22:43:01 +00009990 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009991 fd = open(fname, O_RDONLY);
9992 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009993 if (flags & INPUT_NOFILE_OK)
9994 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009995 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009996 }
Eric Andersenc470f442003-07-28 09:56:35 +00009997 if (fd < 10) {
9998 fd2 = copyfd(fd, 10);
9999 close(fd);
10000 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010001 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +000010002 fd = fd2;
10003 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010004 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010005 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000010006 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010007 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010008}
10009
Eric Andersencb57d552001-06-28 07:25:16 +000010010/*
10011 * Like setinputfile, but takes input from a string.
10012 */
Eric Andersenc470f442003-07-28 09:56:35 +000010013static void
10014setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000010015{
Denis Vlasenkob012b102007-02-19 22:43:01 +000010016 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010017 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010018 g_parsefile->next_to_pgetc = string;
10019 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010020 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010021 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010022 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010023}
10024
10025
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010026/* ============ mail.c
10027 *
10028 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010029 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010030
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010031#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010032
Eric Andersencb57d552001-06-28 07:25:16 +000010033#define MAXMBOXES 10
10034
Eric Andersenc470f442003-07-28 09:56:35 +000010035/* times of mailboxes */
10036static time_t mailtime[MAXMBOXES];
10037/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010038static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010039
Eric Andersencb57d552001-06-28 07:25:16 +000010040/*
Eric Andersenc470f442003-07-28 09:56:35 +000010041 * Print appropriate message(s) if mail has arrived.
10042 * If mail_var_path_changed is set,
10043 * then the value of MAIL has mail_var_path_changed,
10044 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010045 */
Eric Andersenc470f442003-07-28 09:56:35 +000010046static void
10047chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010048{
Eric Andersencb57d552001-06-28 07:25:16 +000010049 const char *mpath;
10050 char *p;
10051 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010052 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010053 struct stackmark smark;
10054 struct stat statb;
10055
Eric Andersencb57d552001-06-28 07:25:16 +000010056 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010057 mpath = mpathset() ? mpathval() : mailval();
10058 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010059 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010060 if (p == NULL)
10061 break;
10062 if (*p == '\0')
10063 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010064 for (q = p; *q; q++)
10065 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010066#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010067 if (q[-1] != '/')
10068 abort();
10069#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010070 q[-1] = '\0'; /* delete trailing '/' */
10071 if (stat(p, &statb) < 0) {
10072 *mtp = 0;
10073 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010074 }
Eric Andersenc470f442003-07-28 09:56:35 +000010075 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10076 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010077 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010078 pathopt ? pathopt : "you have mail"
10079 );
10080 }
10081 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010082 }
Eric Andersenc470f442003-07-28 09:56:35 +000010083 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010084 popstackmark(&smark);
10085}
Eric Andersencb57d552001-06-28 07:25:16 +000010086
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010087static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010088changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010089{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010090 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010091}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010092
Denis Vlasenko131ae172007-02-18 13:00:19 +000010093#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010094
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010095
10096/* ============ ??? */
10097
Eric Andersencb57d552001-06-28 07:25:16 +000010098/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010099 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010100 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010101static void
10102setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010103{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010104 char **newparam;
10105 char **ap;
10106 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010107
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010108 for (nparam = 0; argv[nparam]; nparam++)
10109 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010110 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10111 while (*argv) {
10112 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010113 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010114 *ap = NULL;
10115 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010116 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010117 shellparam.nparam = nparam;
10118 shellparam.p = newparam;
10119#if ENABLE_ASH_GETOPTS
10120 shellparam.optind = 1;
10121 shellparam.optoff = -1;
10122#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010123}
10124
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010125/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010126 * Process shell options. The global variable argptr contains a pointer
10127 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010128 *
10129 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10130 * For a non-interactive shell, an error condition encountered
10131 * by a special built-in ... shall cause the shell to write a diagnostic message
10132 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010133 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010134 * ...
10135 * Utility syntax error (option or operand error) Shall exit
10136 * ...
10137 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10138 * we see that bash does not do that (set "finishes" with error code 1 instead,
10139 * and shell continues), and people rely on this behavior!
10140 * Testcase:
10141 * set -o barfoo 2>/dev/null
10142 * echo $?
10143 *
10144 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010145 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010146static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010147plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010148{
10149 int i;
10150
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010151 if (name) {
10152 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010153 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010154 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010155 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010156 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010157 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010158 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010159 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010160 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010161 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010162 if (val) {
10163 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10164 } else {
10165 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10166 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010167 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010168 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010169}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010170static void
10171setoption(int flag, int val)
10172{
10173 int i;
10174
10175 for (i = 0; i < NOPTS; i++) {
10176 if (optletters(i) == flag) {
10177 optlist[i] = val;
10178 return;
10179 }
10180 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010181 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010182 /* NOTREACHED */
10183}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010184static int
Eric Andersenc470f442003-07-28 09:56:35 +000010185options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010186{
10187 char *p;
10188 int val;
10189 int c;
10190
10191 if (cmdline)
10192 minusc = NULL;
10193 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010194 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010195 if (c != '-' && c != '+')
10196 break;
10197 argptr++;
10198 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010199 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010200 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010201 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010202 if (!cmdline) {
10203 /* "-" means turn off -x and -v */
10204 if (p[0] == '\0')
10205 xflag = vflag = 0;
10206 /* "--" means reset params */
10207 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010208 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010209 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010210 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010211 }
Eric Andersencb57d552001-06-28 07:25:16 +000010212 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010213 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010214 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010215 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010216 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010217 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010218 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010219 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010220 /* it already printed err message */
10221 return 1; /* error */
10222 }
Eric Andersencb57d552001-06-28 07:25:16 +000010223 if (*argptr)
10224 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010225 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10226 isloginsh = 1;
10227 /* bash does not accept +-login, we also won't */
10228 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010229 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010230 isloginsh = 1;
10231 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010232 } else {
10233 setoption(c, val);
10234 }
10235 }
10236 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010237 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010238}
10239
Eric Andersencb57d552001-06-28 07:25:16 +000010240/*
Eric Andersencb57d552001-06-28 07:25:16 +000010241 * The shift builtin command.
10242 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010243static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010244shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010245{
10246 int n;
10247 char **ap1, **ap2;
10248
10249 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010250 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010251 n = number(argv[1]);
10252 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010253 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010254 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010255 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010256 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010257 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010258 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010259 }
10260 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010261 while ((*ap2++ = *ap1++) != NULL)
10262 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010263#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010264 shellparam.optind = 1;
10265 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010266#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010267 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010268 return 0;
10269}
10270
Eric Andersencb57d552001-06-28 07:25:16 +000010271/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010272 * POSIX requires that 'set' (but not export or readonly) output the
10273 * variables in lexicographic order - by the locale's collating order (sigh).
10274 * Maybe we could keep them in an ordered balanced binary tree
10275 * instead of hashed lists.
10276 * For now just roll 'em through qsort for printing...
10277 */
10278static int
10279showvars(const char *sep_prefix, int on, int off)
10280{
10281 const char *sep;
10282 char **ep, **epend;
10283
10284 ep = listvars(on, off, &epend);
10285 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10286
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010287 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010288
10289 for (; ep < epend; ep++) {
10290 const char *p;
10291 const char *q;
10292
10293 p = strchrnul(*ep, '=');
10294 q = nullstr;
10295 if (*p)
10296 q = single_quote(++p);
10297 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10298 }
10299 return 0;
10300}
10301
10302/*
Eric Andersencb57d552001-06-28 07:25:16 +000010303 * The set command builtin.
10304 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010305static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010306setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010307{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010308 int retval;
10309
Denis Vlasenko68404f12008-03-17 09:00:54 +000010310 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010311 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010312
Denis Vlasenkob012b102007-02-19 22:43:01 +000010313 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010314 retval = options(/*cmdline:*/ 0);
10315 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010316 optschanged();
10317 if (*argptr != NULL) {
10318 setparam(argptr);
10319 }
Eric Andersencb57d552001-06-28 07:25:16 +000010320 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010321 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010322 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010323}
10324
Denis Vlasenko131ae172007-02-18 13:00:19 +000010325#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010326static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010327change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010328{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010329 uint32_t t;
10330
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010331 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010332 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010333 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010334 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010335 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010336 vrandom.flags &= ~VNOFUNC;
10337 } else {
10338 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010339 t = strtoul(value, NULL, 10);
10340 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010341 }
Eric Andersenef02f822004-03-11 13:34:24 +000010342}
Eric Andersen16767e22004-03-16 05:14:10 +000010343#endif
10344
Denis Vlasenko131ae172007-02-18 13:00:19 +000010345#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010346static int
Eric Andersenc470f442003-07-28 09:56:35 +000010347getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010348{
10349 char *p, *q;
10350 char c = '?';
10351 int done = 0;
10352 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010353 char s[12];
10354 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010355
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010356 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010357 return 1;
10358 optnext = optfirst + *param_optind - 1;
10359
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010360 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010361 p = NULL;
10362 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010363 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010364 if (p == NULL || *p == '\0') {
10365 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010366 p = *optnext;
10367 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010368 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010369 p = NULL;
10370 done = 1;
10371 goto out;
10372 }
10373 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010374 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010375 goto atend;
10376 }
10377
10378 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010379 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010380 if (*q == '\0') {
10381 if (optstr[0] == ':') {
10382 s[0] = c;
10383 s[1] = '\0';
10384 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010385 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010386 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010387 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010388 }
10389 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010390 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010391 }
10392 if (*++q == ':')
10393 q++;
10394 }
10395
10396 if (*++q == ':') {
10397 if (*p == '\0' && (p = *optnext) == NULL) {
10398 if (optstr[0] == ':') {
10399 s[0] = c;
10400 s[1] = '\0';
10401 err |= setvarsafe("OPTARG", s, 0);
10402 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010403 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010404 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010405 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010406 c = '?';
10407 }
Eric Andersenc470f442003-07-28 09:56:35 +000010408 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010409 }
10410
10411 if (p == *optnext)
10412 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010413 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010414 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010415 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010416 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010417 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010418 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010419 *param_optind = optnext - optfirst + 1;
10420 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010421 err |= setvarsafe("OPTIND", s, VNOFUNC);
10422 s[0] = c;
10423 s[1] = '\0';
10424 err |= setvarsafe(optvar, s, 0);
10425 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010426 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010427 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010428 flush_stdout_stderr();
10429 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010430 }
10431 return done;
10432}
Eric Andersenc470f442003-07-28 09:56:35 +000010433
10434/*
10435 * The getopts builtin. Shellparam.optnext points to the next argument
10436 * to be processed. Shellparam.optptr points to the next character to
10437 * be processed in the current argument. If shellparam.optnext is NULL,
10438 * then it's the first time getopts has been called.
10439 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010440static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010441getoptscmd(int argc, char **argv)
10442{
10443 char **optbase;
10444
10445 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010446 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010447 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010448 optbase = shellparam.p;
10449 if (shellparam.optind > shellparam.nparam + 1) {
10450 shellparam.optind = 1;
10451 shellparam.optoff = -1;
10452 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010453 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010454 optbase = &argv[3];
10455 if (shellparam.optind > argc - 2) {
10456 shellparam.optind = 1;
10457 shellparam.optoff = -1;
10458 }
10459 }
10460
10461 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010462 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010463}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010464#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010465
Eric Andersencb57d552001-06-28 07:25:16 +000010466
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010467/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010468
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010469struct heredoc {
10470 struct heredoc *next; /* next here document in list */
10471 union node *here; /* redirection node */
10472 char *eofmark; /* string indicating end of input */
10473 smallint striptabs; /* if set, strip leading tabs */
10474};
10475
10476static smallint tokpushback; /* last token pushed back */
10477static smallint parsebackquote; /* nonzero if we are inside backquotes */
10478static smallint quoteflag; /* set if (part of) last token was quoted */
10479static token_id_t lasttoken; /* last token read (integer id Txxx) */
10480static struct heredoc *heredoclist; /* list of here documents to read */
10481static char *wordtext; /* text of last word returned by readtoken */
10482static struct nodelist *backquotelist;
10483static union node *redirnode;
10484static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010485
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010486static const char *
10487tokname(char *buf, int tok)
10488{
10489 if (tok < TSEMI)
10490 return tokname_array[tok] + 1;
10491 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10492 return buf;
10493}
10494
10495/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010496 * Called when an unexpected token is read during the parse. The argument
10497 * is the token that is expected, or -1 if more than one type of token can
10498 * occur at this point.
10499 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010500static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010501static void
10502raise_error_unexpected_syntax(int token)
10503{
10504 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010505 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010506 int l;
10507
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010508 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010509 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010510 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010511 raise_error_syntax(msg);
10512 /* NOTREACHED */
10513}
Eric Andersencb57d552001-06-28 07:25:16 +000010514
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010515#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010516
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010517/* parsing is heavily cross-recursive, need these forward decls */
10518static union node *andor(void);
10519static union node *pipeline(void);
10520static union node *parse_command(void);
10521static void parseheredoc(void);
10522static char peektoken(void);
10523static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010524
Eric Andersenc470f442003-07-28 09:56:35 +000010525static union node *
10526list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010527{
10528 union node *n1, *n2, *n3;
10529 int tok;
10530
Eric Andersenc470f442003-07-28 09:56:35 +000010531 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10532 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010533 return NULL;
10534 n1 = NULL;
10535 for (;;) {
10536 n2 = andor();
10537 tok = readtoken();
10538 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010539 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010540 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010541 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010542 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010543 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010544 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010545 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010546 n2 = n3;
10547 }
10548 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010549 }
10550 }
10551 if (n1 == NULL) {
10552 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010553 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010554 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010555 n3->type = NSEMI;
10556 n3->nbinary.ch1 = n1;
10557 n3->nbinary.ch2 = n2;
10558 n1 = n3;
10559 }
10560 switch (tok) {
10561 case TBACKGND:
10562 case TSEMI:
10563 tok = readtoken();
10564 /* fall through */
10565 case TNL:
10566 if (tok == TNL) {
10567 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010568 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010569 return n1;
10570 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010571 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010572 }
Eric Andersenc470f442003-07-28 09:56:35 +000010573 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010574 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010575 return n1;
10576 break;
10577 case TEOF:
10578 if (heredoclist)
10579 parseheredoc();
10580 else
Eric Andersenc470f442003-07-28 09:56:35 +000010581 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010582 return n1;
10583 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010584 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010585 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010586 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010587 return n1;
10588 }
10589 }
10590}
10591
Eric Andersenc470f442003-07-28 09:56:35 +000010592static union node *
10593andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010594{
Eric Andersencb57d552001-06-28 07:25:16 +000010595 union node *n1, *n2, *n3;
10596 int t;
10597
Eric Andersencb57d552001-06-28 07:25:16 +000010598 n1 = pipeline();
10599 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010600 t = readtoken();
10601 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010602 t = NAND;
10603 } else if (t == TOR) {
10604 t = NOR;
10605 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010606 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010607 return n1;
10608 }
Eric Andersenc470f442003-07-28 09:56:35 +000010609 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010610 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010611 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010612 n3->type = t;
10613 n3->nbinary.ch1 = n1;
10614 n3->nbinary.ch2 = n2;
10615 n1 = n3;
10616 }
10617}
10618
Eric Andersenc470f442003-07-28 09:56:35 +000010619static union node *
10620pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010621{
Eric Andersencb57d552001-06-28 07:25:16 +000010622 union node *n1, *n2, *pipenode;
10623 struct nodelist *lp, *prev;
10624 int negate;
10625
10626 negate = 0;
10627 TRACE(("pipeline: entered\n"));
10628 if (readtoken() == TNOT) {
10629 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010630 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010631 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010632 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010633 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010634 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010635 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010636 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010637 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010638 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010639 pipenode->npipe.cmdlist = lp;
10640 lp->n = n1;
10641 do {
10642 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010643 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010644 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010645 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010646 prev->next = lp;
10647 } while (readtoken() == TPIPE);
10648 lp->next = NULL;
10649 n1 = pipenode;
10650 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010651 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010652 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010653 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010654 n2->type = NNOT;
10655 n2->nnot.com = n1;
10656 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010657 }
10658 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010659}
10660
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010661static union node *
10662makename(void)
10663{
10664 union node *n;
10665
Denis Vlasenko597906c2008-02-20 16:38:54 +000010666 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010667 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010668 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010669 n->narg.text = wordtext;
10670 n->narg.backquote = backquotelist;
10671 return n;
10672}
10673
10674static void
10675fixredir(union node *n, const char *text, int err)
10676{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010677 int fd;
10678
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010679 TRACE(("Fix redir %s %d\n", text, err));
10680 if (!err)
10681 n->ndup.vname = NULL;
10682
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010683 fd = bb_strtou(text, NULL, 10);
10684 if (!errno && fd >= 0)
10685 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010686 else if (LONE_DASH(text))
10687 n->ndup.dupfd = -1;
10688 else {
10689 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010690 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010691 n->ndup.vname = makename();
10692 }
10693}
10694
10695/*
10696 * Returns true if the text contains nothing to expand (no dollar signs
10697 * or backquotes).
10698 */
10699static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010700noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010701{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010702 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010703
Denys Vlasenkocd716832009-11-28 22:14:02 +010010704 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010705 if (c == CTLQUOTEMARK)
10706 continue;
10707 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010708 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010709 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010710 return 0;
10711 }
10712 return 1;
10713}
10714
10715static void
10716parsefname(void)
10717{
10718 union node *n = redirnode;
10719
10720 if (readtoken() != TWORD)
10721 raise_error_unexpected_syntax(-1);
10722 if (n->type == NHERE) {
10723 struct heredoc *here = heredoc;
10724 struct heredoc *p;
10725 int i;
10726
10727 if (quoteflag == 0)
10728 n->type = NXHERE;
10729 TRACE(("Here document %d\n", n->type));
10730 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010731 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010732 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733 here->eofmark = wordtext;
10734 here->next = NULL;
10735 if (heredoclist == NULL)
10736 heredoclist = here;
10737 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010738 for (p = heredoclist; p->next; p = p->next)
10739 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010740 p->next = here;
10741 }
10742 } else if (n->type == NTOFD || n->type == NFROMFD) {
10743 fixredir(n, wordtext, 0);
10744 } else {
10745 n->nfile.fname = makename();
10746 }
10747}
Eric Andersencb57d552001-06-28 07:25:16 +000010748
Eric Andersenc470f442003-07-28 09:56:35 +000010749static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010750simplecmd(void)
10751{
10752 union node *args, **app;
10753 union node *n = NULL;
10754 union node *vars, **vpp;
10755 union node **rpp, *redir;
10756 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010757#if ENABLE_ASH_BASH_COMPAT
10758 smallint double_brackets_flag = 0;
10759#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010760
10761 args = NULL;
10762 app = &args;
10763 vars = NULL;
10764 vpp = &vars;
10765 redir = NULL;
10766 rpp = &redir;
10767
10768 savecheckkwd = CHKALIAS;
10769 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010770 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010771 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010772 t = readtoken();
10773 switch (t) {
10774#if ENABLE_ASH_BASH_COMPAT
10775 case TAND: /* "&&" */
10776 case TOR: /* "||" */
10777 if (!double_brackets_flag) {
10778 tokpushback = 1;
10779 goto out;
10780 }
10781 wordtext = (char *) (t == TAND ? "-a" : "-o");
10782#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010783 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010784 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010785 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010786 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010787 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010788#if ENABLE_ASH_BASH_COMPAT
10789 if (strcmp("[[", wordtext) == 0)
10790 double_brackets_flag = 1;
10791 else if (strcmp("]]", wordtext) == 0)
10792 double_brackets_flag = 0;
10793#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010794 n->narg.backquote = backquotelist;
10795 if (savecheckkwd && isassignment(wordtext)) {
10796 *vpp = n;
10797 vpp = &n->narg.next;
10798 } else {
10799 *app = n;
10800 app = &n->narg.next;
10801 savecheckkwd = 0;
10802 }
10803 break;
10804 case TREDIR:
10805 *rpp = n = redirnode;
10806 rpp = &n->nfile.next;
10807 parsefname(); /* read name of redirection file */
10808 break;
10809 case TLP:
10810 if (args && app == &args->narg.next
10811 && !vars && !redir
10812 ) {
10813 struct builtincmd *bcmd;
10814 const char *name;
10815
10816 /* We have a function */
10817 if (readtoken() != TRP)
10818 raise_error_unexpected_syntax(TRP);
10819 name = n->narg.text;
10820 if (!goodname(name)
10821 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10822 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010823 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010824 }
10825 n->type = NDEFUN;
10826 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10827 n->narg.next = parse_command();
10828 return n;
10829 }
10830 /* fall through */
10831 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010832 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010833 goto out;
10834 }
10835 }
10836 out:
10837 *app = NULL;
10838 *vpp = NULL;
10839 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010840 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010841 n->type = NCMD;
10842 n->ncmd.args = args;
10843 n->ncmd.assign = vars;
10844 n->ncmd.redirect = redir;
10845 return n;
10846}
10847
10848static union node *
10849parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010850{
Eric Andersencb57d552001-06-28 07:25:16 +000010851 union node *n1, *n2;
10852 union node *ap, **app;
10853 union node *cp, **cpp;
10854 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010855 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010856 int t;
10857
10858 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010859 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010860
Eric Andersencb57d552001-06-28 07:25:16 +000010861 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010862 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010863 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010864 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010865 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010866 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010867 n1->type = NIF;
10868 n1->nif.test = list(0);
10869 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010870 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010871 n1->nif.ifpart = list(0);
10872 n2 = n1;
10873 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010874 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010875 n2 = n2->nif.elsepart;
10876 n2->type = NIF;
10877 n2->nif.test = list(0);
10878 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010879 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010880 n2->nif.ifpart = list(0);
10881 }
10882 if (lasttoken == TELSE)
10883 n2->nif.elsepart = list(0);
10884 else {
10885 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010886 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010887 }
Eric Andersenc470f442003-07-28 09:56:35 +000010888 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010889 break;
10890 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010891 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010892 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010893 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010894 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010895 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010896 got = readtoken();
10897 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010898 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010899 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010900 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010901 }
10902 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010903 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010904 break;
10905 }
10906 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010907 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010908 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010909 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010910 n1->type = NFOR;
10911 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010912 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010913 if (readtoken() == TIN) {
10914 app = &ap;
10915 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010916 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010917 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010918 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010919 n2->narg.text = wordtext;
10920 n2->narg.backquote = backquotelist;
10921 *app = n2;
10922 app = &n2->narg.next;
10923 }
10924 *app = NULL;
10925 n1->nfor.args = ap;
10926 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010927 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010928 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010929 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010930 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010931 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010932 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010933 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010934 n1->nfor.args = n2;
10935 /*
10936 * Newline or semicolon here is optional (but note
10937 * that the original Bourne shell only allowed NL).
10938 */
10939 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010940 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010941 }
Eric Andersenc470f442003-07-28 09:56:35 +000010942 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010943 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010944 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010945 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010946 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010947 break;
10948 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010949 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010950 n1->type = NCASE;
10951 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010952 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010953 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010954 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010955 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010956 n2->narg.text = wordtext;
10957 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010958 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010959 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010960 } while (readtoken() == TNL);
10961 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010962 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010963 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010964 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010965 checkkwd = CHKNL | CHKKWD;
10966 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010967 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010968 if (lasttoken == TLP)
10969 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010970 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010971 cp->type = NCLIST;
10972 app = &cp->nclist.pattern;
10973 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010974 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010975 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010976 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010977 ap->narg.text = wordtext;
10978 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010979 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010980 break;
10981 app = &ap->narg.next;
10982 readtoken();
10983 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010984 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010985 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010986 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010987 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010988
Eric Andersenc470f442003-07-28 09:56:35 +000010989 cpp = &cp->nclist.next;
10990
10991 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010992 t = readtoken();
10993 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010994 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010995 raise_error_unexpected_syntax(TENDCASE);
10996 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010997 }
Eric Andersenc470f442003-07-28 09:56:35 +000010998 }
Eric Andersencb57d552001-06-28 07:25:16 +000010999 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000011000 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000011001 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000011002 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011003 n1->type = NSUBSHELL;
11004 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011005 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011006 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000011007 break;
11008 case TBEGIN:
11009 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011010 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000011011 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011012 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000011013 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011014 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011015 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000011016 }
11017
Eric Andersenc470f442003-07-28 09:56:35 +000011018 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011019 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000011020
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011021 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000011022 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000011023 checkkwd = CHKKWD | CHKALIAS;
11024 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011025 while (readtoken() == TREDIR) {
11026 *rpp = n2 = redirnode;
11027 rpp = &n2->nfile.next;
11028 parsefname();
11029 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011030 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011031 *rpp = NULL;
11032 if (redir) {
11033 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011034 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011035 n2->type = NREDIR;
11036 n2->nredir.n = n1;
11037 n1 = n2;
11038 }
11039 n1->nredir.redirect = redir;
11040 }
Eric Andersencb57d552001-06-28 07:25:16 +000011041 return n1;
11042}
11043
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011044#if ENABLE_ASH_BASH_COMPAT
11045static int decode_dollar_squote(void)
11046{
11047 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11048 int c, cnt;
11049 char *p;
11050 char buf[4];
11051
11052 c = pgetc();
11053 p = strchr(C_escapes, c);
11054 if (p) {
11055 buf[0] = c;
11056 p = buf;
11057 cnt = 3;
11058 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11059 do {
11060 c = pgetc();
11061 *++p = c;
11062 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11063 pungetc();
11064 } else if (c == 'x') { /* \xHH */
11065 do {
11066 c = pgetc();
11067 *++p = c;
11068 } while (isxdigit(c) && --cnt);
11069 pungetc();
11070 if (cnt == 3) { /* \x but next char is "bad" */
11071 c = 'x';
11072 goto unrecognized;
11073 }
11074 } else { /* simple seq like \\ or \t */
11075 p++;
11076 }
11077 *p = '\0';
11078 p = buf;
11079 c = bb_process_escape_sequence((void*)&p);
11080 } else { /* unrecognized "\z": print both chars unless ' or " */
11081 if (c != '\'' && c != '"') {
11082 unrecognized:
11083 c |= 0x100; /* "please encode \, then me" */
11084 }
11085 }
11086 return c;
11087}
11088#endif
11089
Eric Andersencb57d552001-06-28 07:25:16 +000011090/*
11091 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11092 * is not NULL, read a here document. In the latter case, eofmark is the
11093 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011094 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011095 * is the first character of the input token or document.
11096 *
11097 * Because C does not have internal subroutines, I have simulated them
11098 * using goto's to implement the subroutine linkage. The following macros
11099 * will run code that appears at the end of readtoken1.
11100 */
Eric Andersen2870d962001-07-02 17:27:21 +000011101#define CHECKEND() {goto checkend; checkend_return:;}
11102#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11103#define PARSESUB() {goto parsesub; parsesub_return:;}
11104#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11105#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11106#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011107static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011108readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011109{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011110 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011111 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011112 char *out;
11113 int len;
11114 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011115 struct nodelist *bqlist;
11116 smallint quotef;
11117 smallint dblquote;
11118 smallint oldstyle;
11119 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011120#if ENABLE_ASH_EXPAND_PRMT
11121 smallint pssyntax; /* we are expanding a prompt string */
11122#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011123 int varnest; /* levels of variables expansion */
11124 int arinest; /* levels of arithmetic expansion */
11125 int parenlevel; /* levels of parens in arithmetic */
11126 int dqvarnest; /* levels of variables expansion within double quotes */
11127
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011128 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011129
Eric Andersencb57d552001-06-28 07:25:16 +000011130#if __GNUC__
11131 /* Avoid longjmp clobbering */
11132 (void) &out;
11133 (void) &quotef;
11134 (void) &dblquote;
11135 (void) &varnest;
11136 (void) &arinest;
11137 (void) &parenlevel;
11138 (void) &dqvarnest;
11139 (void) &oldstyle;
11140 (void) &prevsyntax;
11141 (void) &syntax;
11142#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011143 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011144 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011145 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011146 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011147#if ENABLE_ASH_EXPAND_PRMT
11148 pssyntax = (syntax == PSSYNTAX);
11149 if (pssyntax)
11150 syntax = DQSYNTAX;
11151#endif
11152 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011153 varnest = 0;
11154 arinest = 0;
11155 parenlevel = 0;
11156 dqvarnest = 0;
11157
11158 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011159 loop:
11160 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011161 CHECKEND(); /* set c to PEOF if at end of here document */
11162 for (;;) { /* until end of line or end of word */
11163 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11164 switch (SIT(c, syntax)) {
11165 case CNL: /* '\n' */
11166 if (syntax == BASESYNTAX)
11167 goto endword; /* exit outer loop */
11168 USTPUTC(c, out);
11169 g_parsefile->linno++;
11170 setprompt_if(doprompt, 2);
11171 c = pgetc();
11172 goto loop; /* continue outer loop */
11173 case CWORD:
11174 USTPUTC(c, out);
11175 break;
11176 case CCTL:
11177 if (eofmark == NULL || dblquote)
11178 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011179#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011180 if (c == '\\' && bash_dollar_squote) {
11181 c = decode_dollar_squote();
11182 if (c & 0x100) {
11183 USTPUTC('\\', out);
11184 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011185 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011186 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011187#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011188 USTPUTC(c, out);
11189 break;
11190 case CBACK: /* backslash */
11191 c = pgetc_without_PEOA();
11192 if (c == PEOF) {
11193 USTPUTC(CTLESC, out);
11194 USTPUTC('\\', out);
11195 pungetc();
11196 } else if (c == '\n') {
11197 setprompt_if(doprompt, 2);
11198 } else {
11199#if ENABLE_ASH_EXPAND_PRMT
11200 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011201 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011202 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011203 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011204#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011205 /* Backslash is retained if we are in "str" and next char isn't special */
11206 if (dblquote
11207 && c != '\\'
11208 && c != '`'
11209 && c != '$'
11210 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011211 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011212 USTPUTC(CTLESC, out);
11213 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011214 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011215 if (SIT(c, SQSYNTAX) == CCTL)
11216 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011217 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011218 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011219 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011220 break;
11221 case CSQUOTE:
11222 syntax = SQSYNTAX;
11223 quotemark:
11224 if (eofmark == NULL) {
11225 USTPUTC(CTLQUOTEMARK, out);
11226 }
11227 break;
11228 case CDQUOTE:
11229 syntax = DQSYNTAX;
11230 dblquote = 1;
11231 goto quotemark;
11232 case CENDQUOTE:
11233 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11234 if (eofmark != NULL && arinest == 0
11235 && varnest == 0
11236 ) {
11237 USTPUTC(c, out);
11238 } else {
11239 if (dqvarnest == 0) {
11240 syntax = BASESYNTAX;
11241 dblquote = 0;
11242 }
11243 quotef = 1;
11244 goto quotemark;
11245 }
11246 break;
11247 case CVAR: /* '$' */
11248 PARSESUB(); /* parse substitution */
11249 break;
11250 case CENDVAR: /* '}' */
11251 if (varnest > 0) {
11252 varnest--;
11253 if (dqvarnest > 0) {
11254 dqvarnest--;
11255 }
11256 c = CTLENDVAR;
11257 }
11258 USTPUTC(c, out);
11259 break;
11260#if ENABLE_SH_MATH_SUPPORT
11261 case CLP: /* '(' in arithmetic */
11262 parenlevel++;
11263 USTPUTC(c, out);
11264 break;
11265 case CRP: /* ')' in arithmetic */
11266 if (parenlevel > 0) {
11267 parenlevel--;
11268 } else {
11269 if (pgetc() == ')') {
11270 if (--arinest == 0) {
11271 syntax = prevsyntax;
11272 dblquote = (syntax == DQSYNTAX);
11273 c = CTLENDARI;
11274 }
11275 } else {
11276 /*
11277 * unbalanced parens
11278 * (don't 2nd guess - no error)
11279 */
11280 pungetc();
11281 }
11282 }
11283 USTPUTC(c, out);
11284 break;
11285#endif
11286 case CBQUOTE: /* '`' */
11287 PARSEBACKQOLD();
11288 break;
11289 case CENDFILE:
11290 goto endword; /* exit outer loop */
11291 case CIGN:
11292 break;
11293 default:
11294 if (varnest == 0) {
11295#if ENABLE_ASH_BASH_COMPAT
11296 if (c == '&') {
11297 if (pgetc() == '>')
11298 c = 0x100 + '>'; /* flag &> */
11299 pungetc();
11300 }
11301#endif
11302 goto endword; /* exit outer loop */
11303 }
11304 IF_ASH_ALIAS(if (c != PEOA))
11305 USTPUTC(c, out);
11306 }
11307 c = pgetc_fast();
11308 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011309 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011310
Mike Frysinger98c52642009-04-02 10:02:37 +000011311#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011312 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011313 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011314#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011315 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011316 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011317 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011318 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011319 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011320 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011321 }
11322 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011323 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011324 out = stackblock();
11325 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011326 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011327 && quotef == 0
11328 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011329 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011330 PARSEREDIR(); /* passed as params: out, c */
11331 lasttoken = TREDIR;
11332 return lasttoken;
11333 }
11334 /* else: non-number X seen, interpret it
11335 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011336 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011337 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011338 }
11339 quoteflag = quotef;
11340 backquotelist = bqlist;
11341 grabstackblock(len);
11342 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011343 lasttoken = TWORD;
11344 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011345/* end of readtoken routine */
11346
Eric Andersencb57d552001-06-28 07:25:16 +000011347/*
11348 * Check to see whether we are at the end of the here document. When this
11349 * is called, c is set to the first character of the next input line. If
11350 * we are at the end of the here document, this routine sets the c to PEOF.
11351 */
Eric Andersenc470f442003-07-28 09:56:35 +000011352checkend: {
11353 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011354#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011355 if (c == PEOA)
11356 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011357#endif
11358 if (striptabs) {
11359 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011360 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011361 }
Eric Andersenc470f442003-07-28 09:56:35 +000011362 }
11363 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011364 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011365 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011366
Eric Andersenc470f442003-07-28 09:56:35 +000011367 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011368 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11369 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011370 if (*p == '\n' && *q == '\0') {
11371 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011372 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011373 needprompt = doprompt;
11374 } else {
11375 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011376 }
11377 }
11378 }
11379 }
Eric Andersenc470f442003-07-28 09:56:35 +000011380 goto checkend_return;
11381}
Eric Andersencb57d552001-06-28 07:25:16 +000011382
Eric Andersencb57d552001-06-28 07:25:16 +000011383/*
11384 * Parse a redirection operator. The variable "out" points to a string
11385 * specifying the fd to be redirected. The variable "c" contains the
11386 * first character of the redirection operator.
11387 */
Eric Andersenc470f442003-07-28 09:56:35 +000011388parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011389 /* out is already checked to be a valid number or "" */
11390 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011391 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011392
Denis Vlasenko597906c2008-02-20 16:38:54 +000011393 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011394 if (c == '>') {
11395 np->nfile.fd = 1;
11396 c = pgetc();
11397 if (c == '>')
11398 np->type = NAPPEND;
11399 else if (c == '|')
11400 np->type = NCLOBBER;
11401 else if (c == '&')
11402 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011403 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011404 else {
11405 np->type = NTO;
11406 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011407 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011408 }
11409#if ENABLE_ASH_BASH_COMPAT
11410 else if (c == 0x100 + '>') { /* this flags &> redirection */
11411 np->nfile.fd = 1;
11412 pgetc(); /* this is '>', no need to check */
11413 np->type = NTO2;
11414 }
11415#endif
11416 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011417 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011418 c = pgetc();
11419 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011420 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011421 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011422 np = stzalloc(sizeof(struct nhere));
11423 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011424 }
11425 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011426 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011427 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011428 c = pgetc();
11429 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011430 heredoc->striptabs = 1;
11431 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011432 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011433 pungetc();
11434 }
11435 break;
11436
11437 case '&':
11438 np->type = NFROMFD;
11439 break;
11440
11441 case '>':
11442 np->type = NFROMTO;
11443 break;
11444
11445 default:
11446 np->type = NFROM;
11447 pungetc();
11448 break;
11449 }
Eric Andersencb57d552001-06-28 07:25:16 +000011450 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011451 if (fd >= 0)
11452 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011453 redirnode = np;
11454 goto parseredir_return;
11455}
Eric Andersencb57d552001-06-28 07:25:16 +000011456
Eric Andersencb57d552001-06-28 07:25:16 +000011457/*
11458 * Parse a substitution. At this point, we have read the dollar sign
11459 * and nothing else.
11460 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011461
11462/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11463 * (assuming ascii char codes, as the original implementation did) */
11464#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011465 (((unsigned)(c) - 33 < 32) \
11466 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011467parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011468 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011469 int typeloc;
11470 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011471
Eric Andersenc470f442003-07-28 09:56:35 +000011472 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011473 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011474 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011475 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011476#if ENABLE_ASH_BASH_COMPAT
11477 if (c == '\'')
11478 bash_dollar_squote = 1;
11479 else
11480#endif
11481 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011482 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011483 } else if (c == '(') {
11484 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011485 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011486#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011487 PARSEARITH();
11488#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011489 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011490#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011491 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011492 pungetc();
11493 PARSEBACKQNEW();
11494 }
11495 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011496 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011497 USTPUTC(CTLVAR, out);
11498 typeloc = out - (char *)stackblock();
11499 USTPUTC(VSNORMAL, out);
11500 subtype = VSNORMAL;
11501 if (c == '{') {
11502 c = pgetc();
11503 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011504 c = pgetc();
11505 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011506 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011507 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011508 subtype = VSLENGTH; /* ${#VAR} */
11509 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011510 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011511 }
Eric Andersenc470f442003-07-28 09:56:35 +000011512 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011513 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011514 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011515 do {
11516 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011517 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011518 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011519 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011520 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011521 do {
11522 STPUTC(c, out);
11523 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011524 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011525 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011526 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011527 USTPUTC(c, out);
11528 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011529 } else {
11530 badsub:
11531 raise_error_syntax("bad substitution");
11532 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011533 if (c != '}' && subtype == VSLENGTH) {
11534 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011535 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011536 }
Eric Andersencb57d552001-06-28 07:25:16 +000011537
Eric Andersenc470f442003-07-28 09:56:35 +000011538 STPUTC('=', out);
11539 flags = 0;
11540 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011541 /* ${VAR...} but not $VAR or ${#VAR} */
11542 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011543 switch (c) {
11544 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011545 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011546#if ENABLE_ASH_BASH_COMPAT
11547 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011548//TODO: support more general format ${v:EXPR:EXPR},
11549// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011550 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011551 pungetc();
11552 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011553 }
11554#endif
11555 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011556 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011557 default: {
11558 static const char types[] ALIGN1 = "}-+?=";
11559 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011560 if (p == NULL)
11561 goto badsub;
11562 subtype = p - types + VSNORMAL;
11563 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011564 }
Eric Andersenc470f442003-07-28 09:56:35 +000011565 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011566 case '#': {
11567 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011568 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011569 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011570 if (c != cc)
11571 goto do_pungetc;
11572 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011573 break;
11574 }
11575#if ENABLE_ASH_BASH_COMPAT
11576 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011577 /* ${v/[/]pattern/repl} */
11578//TODO: encode pattern and repl separately.
11579// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011580 subtype = VSREPLACE;
11581 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011582 if (c != '/')
11583 goto do_pungetc;
11584 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011585 break;
11586#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011587 }
Eric Andersenc470f442003-07-28 09:56:35 +000011588 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011589 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011590 pungetc();
11591 }
11592 if (dblquote || arinest)
11593 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011594 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011595 if (subtype != VSNORMAL) {
11596 varnest++;
11597 if (dblquote || arinest) {
11598 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011599 }
11600 }
11601 }
Eric Andersenc470f442003-07-28 09:56:35 +000011602 goto parsesub_return;
11603}
Eric Andersencb57d552001-06-28 07:25:16 +000011604
Eric Andersencb57d552001-06-28 07:25:16 +000011605/*
11606 * Called to parse command substitutions. Newstyle is set if the command
11607 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11608 * list of commands (passed by reference), and savelen is the number of
11609 * characters on the top of the stack which must be preserved.
11610 */
Eric Andersenc470f442003-07-28 09:56:35 +000011611parsebackq: {
11612 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011613 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011614 union node *n;
11615 char *volatile str;
11616 struct jmploc jmploc;
11617 struct jmploc *volatile savehandler;
11618 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011619 smallint saveprompt = 0;
11620
Eric Andersencb57d552001-06-28 07:25:16 +000011621#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011622 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011623#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011624 savepbq = parsebackquote;
11625 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011626 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011627 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011628 exception_handler = savehandler;
11629 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011630 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011631 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011632 str = NULL;
11633 savelen = out - (char *)stackblock();
11634 if (savelen > 0) {
11635 str = ckmalloc(savelen);
11636 memcpy(str, stackblock(), savelen);
11637 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011638 savehandler = exception_handler;
11639 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011640 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011641 if (oldstyle) {
11642 /* We must read until the closing backquote, giving special
11643 treatment to some slashes, and then push the string and
11644 reread it as input, interpreting it normally. */
11645 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011646 size_t psavelen;
11647 char *pstr;
11648
Eric Andersenc470f442003-07-28 09:56:35 +000011649 STARTSTACKSTR(pout);
11650 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011651 int pc;
11652
11653 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011654 pc = pgetc();
11655 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011656 case '`':
11657 goto done;
11658
11659 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011660 pc = pgetc();
11661 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011662 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011663 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011664 /*
11665 * If eating a newline, avoid putting
11666 * the newline into the new character
11667 * stream (via the STPUTC after the
11668 * switch).
11669 */
11670 continue;
11671 }
11672 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011673 && (!dblquote || pc != '"')
11674 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011675 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011676 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011677 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011678 break;
11679 }
11680 /* fall through */
11681
11682 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011683 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011684 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011685 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011686
11687 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011688 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011689 needprompt = doprompt;
11690 break;
11691
11692 default:
11693 break;
11694 }
11695 STPUTC(pc, pout);
11696 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011697 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011698 STPUTC('\0', pout);
11699 psavelen = pout - (char *)stackblock();
11700 if (psavelen > 0) {
11701 pstr = grabstackstr(pout);
11702 setinputstring(pstr);
11703 }
11704 }
11705 nlpp = &bqlist;
11706 while (*nlpp)
11707 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011708 *nlpp = stzalloc(sizeof(**nlpp));
11709 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011710 parsebackquote = oldstyle;
11711
11712 if (oldstyle) {
11713 saveprompt = doprompt;
11714 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011715 }
11716
Eric Andersenc470f442003-07-28 09:56:35 +000011717 n = list(2);
11718
11719 if (oldstyle)
11720 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011721 else if (readtoken() != TRP)
11722 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011723
11724 (*nlpp)->n = n;
11725 if (oldstyle) {
11726 /*
11727 * Start reading from old file again, ignoring any pushed back
11728 * tokens left from the backquote parsing
11729 */
11730 popfile();
11731 tokpushback = 0;
11732 }
11733 while (stackblocksize() <= savelen)
11734 growstackblock();
11735 STARTSTACKSTR(out);
11736 if (str) {
11737 memcpy(out, str, savelen);
11738 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011739 INT_OFF;
11740 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011741 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011742 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011743 }
11744 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011745 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011746 if (arinest || dblquote)
11747 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11748 else
11749 USTPUTC(CTLBACKQ, out);
11750 if (oldstyle)
11751 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011752 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011753}
11754
Mike Frysinger98c52642009-04-02 10:02:37 +000011755#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011756/*
11757 * Parse an arithmetic expansion (indicate start of one and set state)
11758 */
Eric Andersenc470f442003-07-28 09:56:35 +000011759parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011760 if (++arinest == 1) {
11761 prevsyntax = syntax;
11762 syntax = ARISYNTAX;
11763 USTPUTC(CTLARI, out);
11764 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011765 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011766 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011767 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011768 } else {
11769 /*
11770 * we collapse embedded arithmetic expansion to
11771 * parenthesis, which should be equivalent
11772 */
11773 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011774 }
Eric Andersenc470f442003-07-28 09:56:35 +000011775 goto parsearith_return;
11776}
11777#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011778
Eric Andersenc470f442003-07-28 09:56:35 +000011779} /* end of readtoken */
11780
Eric Andersencb57d552001-06-28 07:25:16 +000011781/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011782 * Read the next input token.
11783 * If the token is a word, we set backquotelist to the list of cmds in
11784 * backquotes. We set quoteflag to true if any part of the word was
11785 * quoted.
11786 * If the token is TREDIR, then we set redirnode to a structure containing
11787 * the redirection.
11788 * In all cases, the variable startlinno is set to the number of the line
11789 * on which the token starts.
11790 *
11791 * [Change comment: here documents and internal procedures]
11792 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11793 * word parsing code into a separate routine. In this case, readtoken
11794 * doesn't need to have any internal procedures, but parseword does.
11795 * We could also make parseoperator in essence the main routine, and
11796 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011797 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011798#define NEW_xxreadtoken
11799#ifdef NEW_xxreadtoken
11800/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011801static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011802 '\n', '(', ')', /* singles */
11803 '&', '|', ';', /* doubles */
11804 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011805};
Eric Andersencb57d552001-06-28 07:25:16 +000011806
Denis Vlasenko834dee72008-10-07 09:18:30 +000011807#define xxreadtoken_singles 3
11808#define xxreadtoken_doubles 3
11809
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011810static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011811 TNL, TLP, TRP, /* only single occurrence allowed */
11812 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11813 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011814 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011815};
11816
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011817static int
11818xxreadtoken(void)
11819{
11820 int c;
11821
11822 if (tokpushback) {
11823 tokpushback = 0;
11824 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011825 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011826 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011827 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011828 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011829 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011830 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011831 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011832
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011833 if (c == '#') {
11834 while ((c = pgetc()) != '\n' && c != PEOF)
11835 continue;
11836 pungetc();
11837 } else if (c == '\\') {
11838 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011839 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011840 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011841 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011842 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011843 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011844 } else {
11845 const char *p;
11846
11847 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11848 if (c != PEOF) {
11849 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011850 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011851 needprompt = doprompt;
11852 }
11853
11854 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011855 if (p == NULL)
11856 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011857
Denis Vlasenko834dee72008-10-07 09:18:30 +000011858 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11859 int cc = pgetc();
11860 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011861 p += xxreadtoken_doubles + 1;
11862 } else {
11863 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011864#if ENABLE_ASH_BASH_COMPAT
11865 if (c == '&' && cc == '>') /* &> */
11866 break; /* return readtoken1(...) */
11867#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011868 }
11869 }
11870 }
11871 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11872 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011873 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011874 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011875
11876 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011877}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011878#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011879#define RETURN(token) return lasttoken = token
11880static int
11881xxreadtoken(void)
11882{
11883 int c;
11884
11885 if (tokpushback) {
11886 tokpushback = 0;
11887 return lasttoken;
11888 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011889 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011890 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011891 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011892 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011893 switch (c) {
11894 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011895 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011896 continue;
11897 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011898 while ((c = pgetc()) != '\n' && c != PEOF)
11899 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011900 pungetc();
11901 continue;
11902 case '\\':
11903 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011904 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011905 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011906 continue;
11907 }
11908 pungetc();
11909 goto breakloop;
11910 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011911 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011912 needprompt = doprompt;
11913 RETURN(TNL);
11914 case PEOF:
11915 RETURN(TEOF);
11916 case '&':
11917 if (pgetc() == '&')
11918 RETURN(TAND);
11919 pungetc();
11920 RETURN(TBACKGND);
11921 case '|':
11922 if (pgetc() == '|')
11923 RETURN(TOR);
11924 pungetc();
11925 RETURN(TPIPE);
11926 case ';':
11927 if (pgetc() == ';')
11928 RETURN(TENDCASE);
11929 pungetc();
11930 RETURN(TSEMI);
11931 case '(':
11932 RETURN(TLP);
11933 case ')':
11934 RETURN(TRP);
11935 default:
11936 goto breakloop;
11937 }
11938 }
11939 breakloop:
11940 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11941#undef RETURN
11942}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011943#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011944
11945static int
11946readtoken(void)
11947{
11948 int t;
11949#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011950 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011951#endif
11952
11953#if ENABLE_ASH_ALIAS
11954 top:
11955#endif
11956
11957 t = xxreadtoken();
11958
11959 /*
11960 * eat newlines
11961 */
11962 if (checkkwd & CHKNL) {
11963 while (t == TNL) {
11964 parseheredoc();
11965 t = xxreadtoken();
11966 }
11967 }
11968
11969 if (t != TWORD || quoteflag) {
11970 goto out;
11971 }
11972
11973 /*
11974 * check for keywords
11975 */
11976 if (checkkwd & CHKKWD) {
11977 const char *const *pp;
11978
11979 pp = findkwd(wordtext);
11980 if (pp) {
11981 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011982 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011983 goto out;
11984 }
11985 }
11986
11987 if (checkkwd & CHKALIAS) {
11988#if ENABLE_ASH_ALIAS
11989 struct alias *ap;
11990 ap = lookupalias(wordtext, 1);
11991 if (ap != NULL) {
11992 if (*ap->val) {
11993 pushstring(ap->val, ap);
11994 }
11995 goto top;
11996 }
11997#endif
11998 }
11999 out:
12000 checkkwd = 0;
12001#if DEBUG
12002 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020012003 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012004 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020012005 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012006#endif
12007 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000012008}
12009
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012010static char
12011peektoken(void)
12012{
12013 int t;
12014
12015 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012016 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012017 return tokname_array[t][0];
12018}
Eric Andersencb57d552001-06-28 07:25:16 +000012019
12020/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012021 * Read and parse a command. Returns NODE_EOF on end of file.
12022 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000012023 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012024static union node *
12025parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000012026{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012027 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012028
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012029 tokpushback = 0;
12030 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012031 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012032 needprompt = 0;
12033 t = readtoken();
12034 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012035 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012036 if (t == TNL)
12037 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012038 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012039 return list(1);
12040}
12041
12042/*
12043 * Input any here documents.
12044 */
12045static void
12046parseheredoc(void)
12047{
12048 struct heredoc *here;
12049 union node *n;
12050
12051 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012052 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012053
12054 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012055 setprompt_if(needprompt, 2);
12056 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012057 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012058 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012059 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012060 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012061 n->narg.text = wordtext;
12062 n->narg.backquote = backquotelist;
12063 here->here->nhere.doc = n;
12064 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012065 }
Eric Andersencb57d552001-06-28 07:25:16 +000012066}
12067
12068
12069/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012070 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012071 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012072#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012073static const char *
12074expandstr(const char *ps)
12075{
12076 union node n;
12077
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012078 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12079 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012080 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012081 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012082 popfile();
12083
12084 n.narg.type = NARG;
12085 n.narg.next = NULL;
12086 n.narg.text = wordtext;
12087 n.narg.backquote = backquotelist;
12088
12089 expandarg(&n, NULL, 0);
12090 return stackblock();
12091}
12092#endif
12093
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012094/*
12095 * Execute a command or commands contained in a string.
12096 */
12097static int
12098evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012099{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012100 union node *n;
12101 struct stackmark smark;
12102 int skip;
12103
12104 setinputstring(s);
12105 setstackmark(&smark);
12106
12107 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012108 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012109 evaltree(n, 0);
12110 popstackmark(&smark);
12111 skip = evalskip;
12112 if (skip)
12113 break;
12114 }
12115 popfile();
12116
12117 skip &= mask;
12118 evalskip = skip;
12119 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012120}
12121
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012122/*
12123 * The eval command.
12124 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012125static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012126evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012127{
12128 char *p;
12129 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012130
Denis Vlasenko68404f12008-03-17 09:00:54 +000012131 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012132 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012133 argv += 2;
12134 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012135 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012136 for (;;) {
12137 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012138 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012139 if (p == NULL)
12140 break;
12141 STPUTC(' ', concat);
12142 }
12143 STPUTC('\0', concat);
12144 p = grabstackstr(concat);
12145 }
12146 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012147 }
12148 return exitstatus;
12149}
12150
12151/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012152 * Read and execute commands.
12153 * "Top" is nonzero for the top level command loop;
12154 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012155 */
12156static int
12157cmdloop(int top)
12158{
12159 union node *n;
12160 struct stackmark smark;
12161 int inter;
12162 int numeof = 0;
12163
12164 TRACE(("cmdloop(%d) called\n", top));
12165 for (;;) {
12166 int skip;
12167
12168 setstackmark(&smark);
12169#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012170 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012171 showjobs(stderr, SHOW_CHANGED);
12172#endif
12173 inter = 0;
12174 if (iflag && top) {
12175 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012176 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012177 }
12178 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012179#if DEBUG
12180 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012181 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012182#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012183 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012184 if (!top || numeof >= 50)
12185 break;
12186 if (!stoppedjobs()) {
12187 if (!Iflag)
12188 break;
12189 out2str("\nUse \"exit\" to leave shell.\n");
12190 }
12191 numeof++;
12192 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012193 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12194 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012195 numeof = 0;
12196 evaltree(n, 0);
12197 }
12198 popstackmark(&smark);
12199 skip = evalskip;
12200
12201 if (skip) {
12202 evalskip = 0;
12203 return skip & SKIPEVAL;
12204 }
12205 }
12206 return 0;
12207}
12208
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012209/*
12210 * Take commands from a file. To be compatible we should do a path
12211 * search for the file, which is necessary to find sub-commands.
12212 */
12213static char *
12214find_dot_file(char *name)
12215{
12216 char *fullname;
12217 const char *path = pathval();
12218 struct stat statb;
12219
12220 /* don't try this for absolute or relative paths */
12221 if (strchr(name, '/'))
12222 return name;
12223
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012224 /* IIRC standards do not say whether . is to be searched.
12225 * And it is even smaller this way, making it unconditional for now:
12226 */
12227 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12228 fullname = name;
12229 goto try_cur_dir;
12230 }
12231
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012232 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012233 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012234 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12235 /*
12236 * Don't bother freeing here, since it will
12237 * be freed by the caller.
12238 */
12239 return fullname;
12240 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012241 if (fullname != name)
12242 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012243 }
12244
12245 /* not found in the PATH */
12246 ash_msg_and_raise_error("%s: not found", name);
12247 /* NOTREACHED */
12248}
12249
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012250static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012251dotcmd(int argc, char **argv)
12252{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012253 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012254 struct strlist *sp;
12255 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012256
12257 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012258 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012259
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012260 if (!argv[1]) {
12261 /* bash says: "bash: .: filename argument required" */
12262 return 2; /* bash compat */
12263 }
12264
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012265 /* "false; . empty_file; echo $?" should print 0, not 1: */
12266 exitstatus = 0;
12267
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012268 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012269
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012270 argv += 2;
12271 argc -= 2;
12272 if (argc) { /* argc > 0, argv[0] != NULL */
12273 saveparam = shellparam;
12274 shellparam.malloced = 0;
12275 shellparam.nparam = argc;
12276 shellparam.p = argv;
12277 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012278
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012279 setinputfile(fullname, INPUT_PUSH_FILE);
12280 commandname = fullname;
12281 cmdloop(0);
12282 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012283
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012284 if (argc) {
12285 freeparam(&shellparam);
12286 shellparam = saveparam;
12287 };
12288
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012289 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012290}
12291
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012292static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012293exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012294{
12295 if (stoppedjobs())
12296 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012297 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012298 exitstatus = number(argv[1]);
12299 raise_exception(EXEXIT);
12300 /* NOTREACHED */
12301}
12302
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012303/*
12304 * Read a file containing shell functions.
12305 */
12306static void
12307readcmdfile(char *name)
12308{
12309 setinputfile(name, INPUT_PUSH_FILE);
12310 cmdloop(0);
12311 popfile();
12312}
12313
12314
Denis Vlasenkocc571512007-02-23 21:10:35 +000012315/* ============ find_command inplementation */
12316
12317/*
12318 * Resolve a command name. If you change this routine, you may have to
12319 * change the shellexec routine as well.
12320 */
12321static void
12322find_command(char *name, struct cmdentry *entry, int act, const char *path)
12323{
12324 struct tblentry *cmdp;
12325 int idx;
12326 int prev;
12327 char *fullname;
12328 struct stat statb;
12329 int e;
12330 int updatetbl;
12331 struct builtincmd *bcmd;
12332
12333 /* If name contains a slash, don't use PATH or hash table */
12334 if (strchr(name, '/') != NULL) {
12335 entry->u.index = -1;
12336 if (act & DO_ABS) {
12337 while (stat(name, &statb) < 0) {
12338#ifdef SYSV
12339 if (errno == EINTR)
12340 continue;
12341#endif
12342 entry->cmdtype = CMDUNKNOWN;
12343 return;
12344 }
12345 }
12346 entry->cmdtype = CMDNORMAL;
12347 return;
12348 }
12349
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012350/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012351
12352 updatetbl = (path == pathval());
12353 if (!updatetbl) {
12354 act |= DO_ALTPATH;
12355 if (strstr(path, "%builtin") != NULL)
12356 act |= DO_ALTBLTIN;
12357 }
12358
12359 /* If name is in the table, check answer will be ok */
12360 cmdp = cmdlookup(name, 0);
12361 if (cmdp != NULL) {
12362 int bit;
12363
12364 switch (cmdp->cmdtype) {
12365 default:
12366#if DEBUG
12367 abort();
12368#endif
12369 case CMDNORMAL:
12370 bit = DO_ALTPATH;
12371 break;
12372 case CMDFUNCTION:
12373 bit = DO_NOFUNC;
12374 break;
12375 case CMDBUILTIN:
12376 bit = DO_ALTBLTIN;
12377 break;
12378 }
12379 if (act & bit) {
12380 updatetbl = 0;
12381 cmdp = NULL;
12382 } else if (cmdp->rehash == 0)
12383 /* if not invalidated by cd, we're done */
12384 goto success;
12385 }
12386
12387 /* If %builtin not in path, check for builtin next */
12388 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012389 if (bcmd) {
12390 if (IS_BUILTIN_REGULAR(bcmd))
12391 goto builtin_success;
12392 if (act & DO_ALTPATH) {
12393 if (!(act & DO_ALTBLTIN))
12394 goto builtin_success;
12395 } else if (builtinloc <= 0) {
12396 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012397 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012398 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012399
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012400#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012401 {
12402 int applet_no = find_applet_by_name(name);
12403 if (applet_no >= 0) {
12404 entry->cmdtype = CMDNORMAL;
12405 entry->u.index = -2 - applet_no;
12406 return;
12407 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012408 }
12409#endif
12410
Denis Vlasenkocc571512007-02-23 21:10:35 +000012411 /* We have to search path. */
12412 prev = -1; /* where to start */
12413 if (cmdp && cmdp->rehash) { /* doing a rehash */
12414 if (cmdp->cmdtype == CMDBUILTIN)
12415 prev = builtinloc;
12416 else
12417 prev = cmdp->param.index;
12418 }
12419
12420 e = ENOENT;
12421 idx = -1;
12422 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012423 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012424 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012425 /* NB: code below will still use fullname
12426 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012427 idx++;
12428 if (pathopt) {
12429 if (prefix(pathopt, "builtin")) {
12430 if (bcmd)
12431 goto builtin_success;
12432 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012433 }
12434 if ((act & DO_NOFUNC)
12435 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012436 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012437 continue;
12438 }
12439 }
12440 /* if rehash, don't redo absolute path names */
12441 if (fullname[0] == '/' && idx <= prev) {
12442 if (idx < prev)
12443 continue;
12444 TRACE(("searchexec \"%s\": no change\n", name));
12445 goto success;
12446 }
12447 while (stat(fullname, &statb) < 0) {
12448#ifdef SYSV
12449 if (errno == EINTR)
12450 continue;
12451#endif
12452 if (errno != ENOENT && errno != ENOTDIR)
12453 e = errno;
12454 goto loop;
12455 }
12456 e = EACCES; /* if we fail, this will be the error */
12457 if (!S_ISREG(statb.st_mode))
12458 continue;
12459 if (pathopt) { /* this is a %func directory */
12460 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012461 /* NB: stalloc will return space pointed by fullname
12462 * (because we don't have any intervening allocations
12463 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012464 readcmdfile(fullname);
12465 cmdp = cmdlookup(name, 0);
12466 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12467 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12468 stunalloc(fullname);
12469 goto success;
12470 }
12471 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12472 if (!updatetbl) {
12473 entry->cmdtype = CMDNORMAL;
12474 entry->u.index = idx;
12475 return;
12476 }
12477 INT_OFF;
12478 cmdp = cmdlookup(name, 1);
12479 cmdp->cmdtype = CMDNORMAL;
12480 cmdp->param.index = idx;
12481 INT_ON;
12482 goto success;
12483 }
12484
12485 /* We failed. If there was an entry for this command, delete it */
12486 if (cmdp && updatetbl)
12487 delete_cmd_entry();
12488 if (act & DO_ERR)
12489 ash_msg("%s: %s", name, errmsg(e, "not found"));
12490 entry->cmdtype = CMDUNKNOWN;
12491 return;
12492
12493 builtin_success:
12494 if (!updatetbl) {
12495 entry->cmdtype = CMDBUILTIN;
12496 entry->u.cmd = bcmd;
12497 return;
12498 }
12499 INT_OFF;
12500 cmdp = cmdlookup(name, 1);
12501 cmdp->cmdtype = CMDBUILTIN;
12502 cmdp->param.cmd = bcmd;
12503 INT_ON;
12504 success:
12505 cmdp->rehash = 0;
12506 entry->cmdtype = cmdp->cmdtype;
12507 entry->u = cmdp->param;
12508}
12509
12510
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012511/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012512
Eric Andersencb57d552001-06-28 07:25:16 +000012513/*
Eric Andersencb57d552001-06-28 07:25:16 +000012514 * The trap builtin.
12515 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012516static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012517trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012518{
12519 char *action;
12520 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012521 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012522
Eric Andersenc470f442003-07-28 09:56:35 +000012523 nextopt(nullstr);
12524 ap = argptr;
12525 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012526 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012527 char *tr = trap_ptr[signo];
12528 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012529 /* note: bash adds "SIG", but only if invoked
12530 * as "bash". If called as "sh", or if set -o posix,
12531 * then it prints short signal names.
12532 * We are printing short names: */
12533 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012534 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012535 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012536 /* trap_ptr != trap only if we are in special-cased `trap` code.
12537 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012538 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012539 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012540 }
12541 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012542 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012543 if (trap_ptr != trap) {
12544 free(trap_ptr);
12545 trap_ptr = trap;
12546 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012547 */
Eric Andersencb57d552001-06-28 07:25:16 +000012548 return 0;
12549 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012550
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012551 action = NULL;
12552 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012553 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012554 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012555 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012556 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012557 if (signo < 0) {
12558 /* Mimic bash message exactly */
12559 ash_msg("%s: invalid signal specification", *ap);
12560 exitcode = 1;
12561 goto next;
12562 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012563 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012564 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012565 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012566 action = NULL;
12567 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012568 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012569 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012570 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012571 if (action)
12572 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012573 trap[signo] = action;
12574 if (signo != 0)
12575 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012576 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012577 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012578 ap++;
12579 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012580 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012581}
12582
Eric Andersenc470f442003-07-28 09:56:35 +000012583
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012584/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012585
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012586#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012587/*
12588 * Lists available builtins
12589 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012590static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012591helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012592{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012593 unsigned col;
12594 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012595
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012596 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012597 "Built-in commands:\n"
12598 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012599 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012600 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012601 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012602 if (col > 60) {
12603 out1fmt("\n");
12604 col = 0;
12605 }
12606 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012607#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012608 {
12609 const char *a = applet_names;
12610 while (*a) {
12611 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12612 if (col > 60) {
12613 out1fmt("\n");
12614 col = 0;
12615 }
12616 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012617 }
12618 }
12619#endif
12620 out1fmt("\n\n");
12621 return EXIT_SUCCESS;
12622}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012623#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012624
Eric Andersencb57d552001-06-28 07:25:16 +000012625/*
Eric Andersencb57d552001-06-28 07:25:16 +000012626 * The export and readonly commands.
12627 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012628static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012629exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012630{
12631 struct var *vp;
12632 char *name;
12633 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012634 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020012635 char opt;
12636 int flag;
12637 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000012638
Denys Vlasenkod5275882012-10-01 13:41:17 +020012639 /* "readonly" in bash accepts, but ignores -n.
12640 * We do the same: it saves a conditional in nextopt's param.
12641 */
12642 flag_off = 0;
12643 while ((opt = nextopt("np")) != '\0') {
12644 if (opt == 'n')
12645 flag_off = VEXPORT;
12646 }
12647 flag = VEXPORT;
12648 if (argv[0][0] == 'r') {
12649 flag = VREADONLY;
12650 flag_off = 0; /* readonly ignores -n */
12651 }
12652 flag_off = ~flag_off;
12653
12654 /*if (opt_p_not_specified) - bash doesnt check this. Try "export -p NAME" */
12655 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012656 aptr = argptr;
12657 name = *aptr;
12658 if (name) {
12659 do {
12660 p = strchr(name, '=');
12661 if (p != NULL) {
12662 p++;
12663 } else {
12664 vp = *findvar(hashvar(name), name);
12665 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020012666 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012667 continue;
12668 }
Eric Andersencb57d552001-06-28 07:25:16 +000012669 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020012670 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012671 } while ((name = *++aptr) != NULL);
12672 return 0;
12673 }
Eric Andersencb57d552001-06-28 07:25:16 +000012674 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020012675
12676 /* No arguments. Show the list of exported or readonly vars.
12677 * -n is ignored.
12678 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012679 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012680 return 0;
12681}
12682
Eric Andersencb57d552001-06-28 07:25:16 +000012683/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012684 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012685 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012686static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012687unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012688{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012689 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012690
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012691 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012692 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012693 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012694}
12695
Eric Andersencb57d552001-06-28 07:25:16 +000012696/*
Eric Andersencb57d552001-06-28 07:25:16 +000012697 * The unset builtin command. We unset the function before we unset the
12698 * variable to allow a function to be unset when there is a readonly variable
12699 * with the same name.
12700 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012701static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012702unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012703{
12704 char **ap;
12705 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012706 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012707 int ret = 0;
12708
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012709 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012710 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012711 }
Eric Andersencb57d552001-06-28 07:25:16 +000012712
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012713 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012714 if (flag != 'f') {
12715 i = unsetvar(*ap);
12716 ret |= i;
12717 if (!(i & 2))
12718 continue;
12719 }
12720 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012721 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012722 }
Eric Andersenc470f442003-07-28 09:56:35 +000012723 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012724}
12725
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012726static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012727 ' ', offsetof(struct tms, tms_utime),
12728 '\n', offsetof(struct tms, tms_stime),
12729 ' ', offsetof(struct tms, tms_cutime),
12730 '\n', offsetof(struct tms, tms_cstime),
12731 0
12732};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012733static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012734timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012735{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012736 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012737 const unsigned char *p;
12738 struct tms buf;
12739
12740 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012741 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012742
12743 p = timescmd_str;
12744 do {
12745 t = *(clock_t *)(((char *) &buf) + p[1]);
12746 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012747 t = t % clk_tck;
12748 out1fmt("%lum%lu.%03lus%c",
12749 s / 60, s % 60,
12750 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012751 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012752 p += 2;
12753 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012754
Eric Andersencb57d552001-06-28 07:25:16 +000012755 return 0;
12756}
12757
Mike Frysinger98c52642009-04-02 10:02:37 +000012758#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012759/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012760 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012761 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012762 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012763 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012764 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012765static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012766letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012767{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012768 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012769
Denis Vlasenko68404f12008-03-17 09:00:54 +000012770 argv++;
12771 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012772 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012773 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012774 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012775 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012776
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012777 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012778}
Eric Andersenc470f442003-07-28 09:56:35 +000012779#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012780
Eric Andersenc470f442003-07-28 09:56:35 +000012781/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012782 * The read builtin. Options:
12783 * -r Do not interpret '\' specially
12784 * -s Turn off echo (tty only)
12785 * -n NCHARS Read NCHARS max
12786 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12787 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12788 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012789 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012790 * TODO: bash also has:
12791 * -a ARRAY Read into array[0],[1],etc
12792 * -d DELIM End on DELIM char, not newline
12793 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012794 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012795static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012796readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012797{
Denys Vlasenko73067272010-01-12 22:11:24 +010012798 char *opt_n = NULL;
12799 char *opt_p = NULL;
12800 char *opt_t = NULL;
12801 char *opt_u = NULL;
12802 int read_flags = 0;
12803 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012804 int i;
12805
Denys Vlasenko73067272010-01-12 22:11:24 +010012806 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012807 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012808 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012809 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012810 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012811 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012812 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012813 break;
12814 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012815 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012816 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012817 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012818 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012819 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012820 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012821 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012822 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012823 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012824 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012825 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012826 default:
12827 break;
12828 }
Eric Andersenc470f442003-07-28 09:56:35 +000012829 }
Paul Fox02eb9342005-09-07 16:56:02 +000012830
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020012831 /* "read -s" needs to save/restore termios, can't allow ^C
12832 * to jump out of it.
12833 */
12834 INT_OFF;
Denys Vlasenko03dad222010-01-12 23:29:57 +010012835 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012836 argptr,
12837 bltinlookup("IFS"), /* can be NULL */
12838 read_flags,
12839 opt_n,
12840 opt_p,
12841 opt_t,
12842 opt_u
12843 );
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020012844 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012845
Denys Vlasenko73067272010-01-12 22:11:24 +010012846 if ((uintptr_t)r > 1)
12847 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012848
Denys Vlasenko73067272010-01-12 22:11:24 +010012849 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012850}
12851
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012852static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012853umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012854{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012855 static const char permuser[3] ALIGN1 = "ugo";
12856 static const char permmode[3] ALIGN1 = "rwx";
12857 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012858 S_IRUSR, S_IWUSR, S_IXUSR,
12859 S_IRGRP, S_IWGRP, S_IXGRP,
12860 S_IROTH, S_IWOTH, S_IXOTH
12861 };
12862
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012863 /* TODO: use bb_parse_mode() instead */
12864
Eric Andersenc470f442003-07-28 09:56:35 +000012865 char *ap;
12866 mode_t mask;
12867 int i;
12868 int symbolic_mode = 0;
12869
12870 while (nextopt("S") != '\0') {
12871 symbolic_mode = 1;
12872 }
12873
Denis Vlasenkob012b102007-02-19 22:43:01 +000012874 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012875 mask = umask(0);
12876 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012877 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012878
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012879 ap = *argptr;
12880 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012881 if (symbolic_mode) {
12882 char buf[18];
12883 char *p = buf;
12884
12885 for (i = 0; i < 3; i++) {
12886 int j;
12887
12888 *p++ = permuser[i];
12889 *p++ = '=';
12890 for (j = 0; j < 3; j++) {
12891 if ((mask & permmask[3 * i + j]) == 0) {
12892 *p++ = permmode[j];
12893 }
12894 }
12895 *p++ = ',';
12896 }
12897 *--p = 0;
12898 puts(buf);
12899 } else {
12900 out1fmt("%.4o\n", mask);
12901 }
12902 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012903 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012904 mask = 0;
12905 do {
12906 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012907 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012908 mask = (mask << 3) + (*ap - '0');
12909 } while (*++ap != '\0');
12910 umask(mask);
12911 } else {
12912 mask = ~mask & 0777;
12913 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012914 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012915 }
12916 umask(~mask & 0777);
12917 }
12918 }
12919 return 0;
12920}
12921
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012922static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012923ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012924{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012925 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012926}
12927
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012928/* ============ main() and helpers */
12929
12930/*
12931 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012932 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012933static void
12934exitshell(void)
12935{
12936 struct jmploc loc;
12937 char *p;
12938 int status;
12939
Denys Vlasenkobede2152011-09-04 16:12:33 +020012940#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
12941 save_history(line_input_state);
12942#endif
12943
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012944 status = exitstatus;
12945 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12946 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012947 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012948/* dash bug: it just does _exit(exitstatus) here
12949 * but we have to do setjobctl(0) first!
12950 * (bug is still not fixed in dash-0.5.3 - if you run dash
12951 * under Midnight Commander, on exit from dash MC is backgrounded) */
12952 status = exitstatus;
12953 goto out;
12954 }
12955 exception_handler = &loc;
12956 p = trap[0];
12957 if (p) {
12958 trap[0] = NULL;
12959 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012960 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012961 }
12962 flush_stdout_stderr();
12963 out:
12964 setjobctl(0);
12965 _exit(status);
12966 /* NOTREACHED */
12967}
12968
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012969static void
12970init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012971{
12972 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012973 /* we will never free this */
12974 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012975
12976 /* from trap.c: */
12977 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012978 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12979 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12980 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012981 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012982
12983 /* from var.c: */
12984 {
12985 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012986 const char *p;
12987 struct stat st1, st2;
12988
12989 initvar();
12990 for (envp = environ; envp && *envp; envp++) {
12991 if (strchr(*envp, '=')) {
12992 setvareq(*envp, VEXPORT|VTEXTFIXED);
12993 }
12994 }
12995
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012996 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012997
12998 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012999 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013000 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013001 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
13002 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013003 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013004 }
13005 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006 setpwd(p, 0);
13007 }
13008}
13009
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013010
13011//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010013012//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013013//usage:#define ash_full_usage "\n\n"
13014//usage: "Unix shell interpreter"
13015
13016//usage:#if ENABLE_FEATURE_SH_IS_ASH
13017//usage:# define sh_trivial_usage ash_trivial_usage
13018//usage:# define sh_full_usage ash_full_usage
13019//usage:#endif
13020//usage:#if ENABLE_FEATURE_BASH_IS_ASH
13021//usage:# define bash_trivial_usage ash_trivial_usage
13022//usage:# define bash_full_usage ash_full_usage
13023//usage:#endif
13024
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013025/*
13026 * Process the shell command line arguments.
13027 */
13028static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000013029procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013030{
13031 int i;
13032 const char *xminusc;
13033 char **xargv;
13034
13035 xargv = argv;
13036 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013037 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013038 xargv++;
13039 for (i = 0; i < NOPTS; i++)
13040 optlist[i] = 2;
13041 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013042 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013043 /* it already printed err message */
13044 raise_exception(EXERROR);
13045 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013046 xargv = argptr;
13047 xminusc = minusc;
13048 if (*xargv == NULL) {
13049 if (xminusc)
13050 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13051 sflag = 1;
13052 }
13053 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13054 iflag = 1;
13055 if (mflag == 2)
13056 mflag = iflag;
13057 for (i = 0; i < NOPTS; i++)
13058 if (optlist[i] == 2)
13059 optlist[i] = 0;
13060#if DEBUG == 2
13061 debug = 1;
13062#endif
13063 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13064 if (xminusc) {
13065 minusc = *xargv++;
13066 if (*xargv)
13067 goto setarg0;
13068 } else if (!sflag) {
13069 setinputfile(*xargv, 0);
13070 setarg0:
13071 arg0 = *xargv++;
13072 commandname = arg0;
13073 }
13074
13075 shellparam.p = xargv;
13076#if ENABLE_ASH_GETOPTS
13077 shellparam.optind = 1;
13078 shellparam.optoff = -1;
13079#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013080 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013081 while (*xargv) {
13082 shellparam.nparam++;
13083 xargv++;
13084 }
13085 optschanged();
13086}
13087
13088/*
13089 * Read /etc/profile or .profile.
13090 */
13091static void
13092read_profile(const char *name)
13093{
13094 int skip;
13095
13096 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13097 return;
13098 skip = cmdloop(0);
13099 popfile();
13100 if (skip)
13101 exitshell();
13102}
13103
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013104/*
13105 * This routine is called when an error or an interrupt occurs in an
13106 * interactive shell and control is returned to the main command loop.
13107 */
13108static void
13109reset(void)
13110{
13111 /* from eval.c: */
13112 evalskip = 0;
13113 loopnest = 0;
13114 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013115 g_parsefile->left_in_buffer = 0;
13116 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013117 popallfiles();
13118 /* from parser.c: */
13119 tokpushback = 0;
13120 checkkwd = 0;
13121 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013122 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013123}
13124
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013125#if PROFILE
13126static short profile_buf[16384];
13127extern int etext();
13128#endif
13129
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013130/*
13131 * Main routine. We initialize things, parse the arguments, execute
13132 * profiles if we're a login shell, and then call cmdloop to execute
13133 * commands. The setjmp call sets up the location to jump to when an
13134 * exception occurs. When an exception occurs the variable "state"
13135 * is used to figure out how far we had gotten.
13136 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013137int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013138int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013139{
Mike Frysinger98c52642009-04-02 10:02:37 +000013140 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013141 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013142 struct jmploc jmploc;
13143 struct stackmark smark;
13144
Denis Vlasenko01631112007-12-16 17:20:38 +000013145 /* Initialize global data */
13146 INIT_G_misc();
13147 INIT_G_memstack();
13148 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013149#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013150 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013151#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013152 INIT_G_cmdtable();
13153
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013154#if PROFILE
13155 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13156#endif
13157
13158#if ENABLE_FEATURE_EDITING
13159 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13160#endif
13161 state = 0;
13162 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013163 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013164 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013165
13166 reset();
13167
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013168 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013169 if (e == EXERROR)
13170 exitstatus = 2;
13171 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013172 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013173 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013174 }
13175 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013176 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013177 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013178
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013179 popstackmark(&smark);
13180 FORCE_INT_ON; /* enable interrupts */
13181 if (s == 1)
13182 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013183 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013184 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013185 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013186 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013187 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013188 }
13189 exception_handler = &jmploc;
13190#if DEBUG
13191 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013192 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013193 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013194#endif
13195 rootpid = getpid();
13196
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013197 init();
13198 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013199 procargs(argv);
13200
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013201#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13202 if (iflag) {
13203 const char *hp = lookupvar("HISTFILE");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013204 if (!hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013205 hp = lookupvar("HOME");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013206 if (hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013207 char *defhp = concat_path_file(hp, ".ash_history");
13208 setvar("HISTFILE", defhp, 0);
13209 free(defhp);
13210 }
13211 }
13212 }
13213#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013214 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013215 isloginsh = 1;
13216 if (isloginsh) {
13217 state = 1;
13218 read_profile("/etc/profile");
13219 state1:
13220 state = 2;
13221 read_profile(".profile");
13222 }
13223 state2:
13224 state = 3;
13225 if (
13226#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013227 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013228#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013229 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013230 ) {
13231 shinit = lookupvar("ENV");
13232 if (shinit != NULL && *shinit != '\0') {
13233 read_profile(shinit);
13234 }
13235 }
13236 state3:
13237 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013238 if (minusc) {
13239 /* evalstring pushes parsefile stack.
13240 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013241 * is one of stacked source fds.
13242 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013243 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013244 // ^^ not necessary since now we special-case fd 0
13245 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013246 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013247 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013248
13249 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020013250#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013251 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013252 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013253 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013254 line_input_state->hist_file = hp;
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013255# if ENABLE_FEATURE_SH_HISTFILESIZE
13256 hp = lookupvar("HISTFILESIZE");
13257 line_input_state->max_history = size_from_HISTFILESIZE(hp);
13258# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013259 }
13260#endif
13261 state4: /* XXX ??? - why isn't this before the "if" statement */
13262 cmdloop(1);
13263 }
13264#if PROFILE
13265 monitor(0);
13266#endif
13267#ifdef GPROF
13268 {
13269 extern void _mcleanup(void);
13270 _mcleanup();
13271 }
13272#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013273 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013274 exitshell();
13275 /* NOTREACHED */
13276}
13277
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013278
Eric Andersendf82f612001-06-28 07:46:40 +000013279/*-
13280 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013281 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013282 *
13283 * This code is derived from software contributed to Berkeley by
13284 * Kenneth Almquist.
13285 *
13286 * Redistribution and use in source and binary forms, with or without
13287 * modification, are permitted provided that the following conditions
13288 * are met:
13289 * 1. Redistributions of source code must retain the above copyright
13290 * notice, this list of conditions and the following disclaimer.
13291 * 2. Redistributions in binary form must reproduce the above copyright
13292 * notice, this list of conditions and the following disclaimer in the
13293 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013294 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013295 * may be used to endorse or promote products derived from this software
13296 * without specific prior written permission.
13297 *
13298 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13299 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13300 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13301 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13302 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13303 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13304 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13305 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13306 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13307 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13308 * SUCH DAMAGE.
13309 */