blob: ced839d521d327a98f666cfae30ccd53d49db615 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenkob012b102007-02-19 22:43:01 +000039#include <paths.h>
40#include <setjmp.h>
41#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020042#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010043
Denys Vlasenko20704f02011-03-23 17:59:27 +010044#include "busybox.h" /* for applet_names */
45#include "unicode.h"
46
Denys Vlasenko73067272010-01-12 22:11:24 +010047#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010048#if ENABLE_SH_MATH_SUPPORT
49# include "math.h"
50#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020051#if ENABLE_ASH_RANDOM_SUPPORT
52# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020053#else
54# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020055#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000056
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020057#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010058#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000059/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020060# undef CONFIG_FEATURE_SH_STANDALONE
61# undef ENABLE_FEATURE_SH_STANDALONE
62# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010063# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020064# define ENABLE_FEATURE_SH_STANDALONE 0
65# define IF_FEATURE_SH_STANDALONE(...)
66# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000067#endif
68
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000070# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000071#endif
72
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010073#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000074# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000075#endif
76
Denys Vlasenko771f1992010-07-16 14:31:34 +020077//config:config ASH
78//config: bool "ash"
79//config: default y
80//config: depends on !NOMMU
81//config: help
82//config: Tha 'ash' shell adds about 60k in the default configuration and is
83//config: the most complete and most pedantically correct shell included with
84//config: busybox. This shell is actually a derivative of the Debian 'dash'
85//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
86//config: (written by Kenneth Almquist) from NetBSD.
87//config:
88//config:config ASH_BASH_COMPAT
89//config: bool "bash-compatible extensions"
90//config: default y
91//config: depends on ASH
92//config: help
93//config: Enable bash-compatible extensions.
94//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +010095//config:config ASH_IDLE_TIMEOUT
96//config: bool "Idle timeout variable"
97//config: default n
98//config: depends on ASH
99//config: help
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100100//config: Enables bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko046341e2011-02-04 17:53:59 +0100101//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200102//config:config ASH_JOB_CONTROL
103//config: bool "Job control"
104//config: default y
105//config: depends on ASH
106//config: help
107//config: Enable job control in the ash shell.
108//config:
109//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100110//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config: default y
112//config: depends on ASH
113//config: help
114//config: Enable alias support in the ash shell.
115//config:
116//config:config ASH_GETOPTS
117//config: bool "Builtin getopt to parse positional parameters"
118//config: default y
119//config: depends on ASH
120//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100121//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200122//config:
123//config:config ASH_BUILTIN_ECHO
124//config: bool "Builtin version of 'echo'"
125//config: default y
126//config: depends on ASH
127//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100128//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config:
130//config:config ASH_BUILTIN_PRINTF
131//config: bool "Builtin version of 'printf'"
132//config: default y
133//config: depends on ASH
134//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100135//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
137//config:config ASH_BUILTIN_TEST
138//config: bool "Builtin version of 'test'"
139//config: default y
140//config: depends on ASH
141//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100142//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200143//config:
144//config:config ASH_CMDCMD
145//config: bool "'command' command to override shell builtins"
146//config: default y
147//config: depends on ASH
148//config: help
149//config: Enable support for the ash 'command' builtin, which allows
150//config: you to run the specified command with the specified arguments,
151//config: even when there is an ash builtin command with the same name.
152//config:
153//config:config ASH_MAIL
154//config: bool "Check for new mail on interactive shells"
155//config: default n
156//config: depends on ASH
157//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100158//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200159//config:
160//config:config ASH_OPTIMIZE_FOR_SIZE
161//config: bool "Optimize for size instead of speed"
162//config: default y
163//config: depends on ASH
164//config: help
165//config: Compile ash for reduced size at the price of speed.
166//config:
167//config:config ASH_RANDOM_SUPPORT
168//config: bool "Pseudorandom generator and $RANDOM variable"
169//config: default y
170//config: depends on ASH
171//config: help
172//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
173//config: Each read of "$RANDOM" will generate a new pseudorandom value.
174//config: You can reset the generator by using a specified start value.
175//config: After "unset RANDOM" the generator will switch off and this
176//config: variable will no longer have special treatment.
177//config:
178//config:config ASH_EXPAND_PRMT
179//config: bool "Expand prompt string"
180//config: default y
181//config: depends on ASH
182//config: help
183//config: "PS#" may contain volatile content, such as backquote commands.
184//config: This option recreates the prompt string from the environment
185//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200186//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200187
Denys Vlasenko20704f02011-03-23 17:59:27 +0100188//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
189//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
190//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
191
192//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
193//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
194
Denis Vlasenkob012b102007-02-19 22:43:01 +0000195
Denis Vlasenko01631112007-12-16 17:20:38 +0000196/* ============ Hash table sizes. Configurable. */
197
198#define VTABSIZE 39
199#define ATABSIZE 39
200#define CMDTABLESIZE 31 /* should be prime */
201
202
Denis Vlasenkob012b102007-02-19 22:43:01 +0000203/* ============ Shell options */
204
205static const char *const optletters_optnames[] = {
206 "e" "errexit",
207 "f" "noglob",
208 "I" "ignoreeof",
209 "i" "interactive",
210 "m" "monitor",
211 "n" "noexec",
212 "s" "stdin",
213 "x" "xtrace",
214 "v" "verbose",
215 "C" "noclobber",
216 "a" "allexport",
217 "b" "notify",
218 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100219 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100220#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100221 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100222#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000223#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000224 ,"\0" "nolog"
225 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000226#endif
227};
228
Denys Vlasenko285ad152009-12-04 23:02:27 +0100229#define optletters(n) optletters_optnames[n][0]
230#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000232enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000233
Eric Andersenc470f442003-07-28 09:56:35 +0000234
Denis Vlasenkob012b102007-02-19 22:43:01 +0000235/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000236
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200237#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000238
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000239/*
Eric Andersenc470f442003-07-28 09:56:35 +0000240 * We enclose jmp_buf in a structure so that we can declare pointers to
241 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000242 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000243 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000244 * exception handlers, the user should save the value of handler on entry
245 * to an inner scope, set handler to point to a jmploc structure for the
246 * inner scope, and restore handler on exit from the scope.
247 */
Eric Andersenc470f442003-07-28 09:56:35 +0000248struct jmploc {
249 jmp_buf loc;
250};
Denis Vlasenko01631112007-12-16 17:20:38 +0000251
252struct globals_misc {
253 /* pid of main shell */
254 int rootpid;
255 /* shell level: 0 for the main shell, 1 for its children, and so on */
256 int shlvl;
257#define rootshell (!shlvl)
258 char *minusc; /* argument to -c option */
259
260 char *curdir; // = nullstr; /* current working directory */
261 char *physdir; // = nullstr; /* physical working directory */
262
263 char *arg0; /* value of $0 */
264
265 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000266
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200267 volatile int suppress_int; /* counter */
268 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000269 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200270 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000271 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000272 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000273#define EXINT 0 /* SIGINT received */
274#define EXERROR 1 /* a generic error */
275#define EXSHELLPROC 2 /* execute a shell procedure */
276#define EXEXEC 3 /* command execution failed */
277#define EXEXIT 4 /* exit the shell */
278#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000279
Denis Vlasenko01631112007-12-16 17:20:38 +0000280 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000281 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000282
283 char optlist[NOPTS];
284#define eflag optlist[0]
285#define fflag optlist[1]
286#define Iflag optlist[2]
287#define iflag optlist[3]
288#define mflag optlist[4]
289#define nflag optlist[5]
290#define sflag optlist[6]
291#define xflag optlist[7]
292#define vflag optlist[8]
293#define Cflag optlist[9]
294#define aflag optlist[10]
295#define bflag optlist[11]
296#define uflag optlist[12]
297#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100298#if ENABLE_ASH_BASH_COMPAT
299# define pipefail optlist[14]
300#else
301# define pipefail 0
302#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000303#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100304# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
305# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000306#endif
307
308 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000309 /*
310 * Sigmode records the current value of the signal handlers for the various
311 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000312 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000313 */
314 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000315#define S_DFL 1 /* default signal handling (SIG_DFL) */
316#define S_CATCH 2 /* signal is caught */
317#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000318#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000319
Denis Vlasenko01631112007-12-16 17:20:38 +0000320 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000321 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200322 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000323 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200324 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000325
326 /* Rarely referenced stuff */
327#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200328 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000329#endif
330 pid_t backgndpid; /* pid of last background process */
331 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000332};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000333extern struct globals_misc *const ash_ptr_to_globals_misc;
334#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000335#define rootpid (G_misc.rootpid )
336#define shlvl (G_misc.shlvl )
337#define minusc (G_misc.minusc )
338#define curdir (G_misc.curdir )
339#define physdir (G_misc.physdir )
340#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000341#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000342#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200343#define suppress_int (G_misc.suppress_int )
344#define pending_int (G_misc.pending_int )
345#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000346#define isloginsh (G_misc.isloginsh )
347#define nullstr (G_misc.nullstr )
348#define optlist (G_misc.optlist )
349#define sigmode (G_misc.sigmode )
350#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200351#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000352#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200353#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200354#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000355#define backgndpid (G_misc.backgndpid )
356#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000357#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000358 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
359 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000360 curdir = nullstr; \
361 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200362 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000363} while (0)
364
365
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000366/* ============ DEBUG */
367#if DEBUG
368static void trace_printf(const char *fmt, ...);
369static void trace_vprintf(const char *fmt, va_list va);
370# define TRACE(param) trace_printf param
371# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000372# define close(fd) do { \
373 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000374 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200375 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000376 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000377} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000378#else
379# define TRACE(param)
380# define TRACEV(param)
381#endif
382
383
Denis Vlasenko559691a2008-10-05 18:39:31 +0000384/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000385#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
386
Denis Vlasenko559691a2008-10-05 18:39:31 +0000387static int isdigit_str9(const char *str)
388{
389 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
390 while (--maxlen && isdigit(*str))
391 str++;
392 return (*str == '\0');
393}
Denis Vlasenko01631112007-12-16 17:20:38 +0000394
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200395static const char *var_end(const char *var)
396{
397 while (*var)
398 if (*var++ == '=')
399 break;
400 return var;
401}
402
Denis Vlasenko559691a2008-10-05 18:39:31 +0000403
404/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100405
406static void exitshell(void) NORETURN;
407
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000408/*
Eric Andersen2870d962001-07-02 17:27:21 +0000409 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000410 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000411 * much more efficient and portable. (But hacking the kernel is so much
412 * more fun than worrying about efficiency and portability. :-))
413 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000414#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200415 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000416 xbarrier(); \
417} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418
419/*
420 * Called to raise an exception. Since C doesn't include exceptions, we
421 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000422 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000423 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000424static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000425static void
426raise_exception(int e)
427{
428#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000429 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000430 abort();
431#endif
432 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000433 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000434 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000435}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000436#if DEBUG
437#define raise_exception(e) do { \
438 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
439 raise_exception(e); \
440} while (0)
441#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000442
443/*
444 * Called from trap.c when a SIGINT is received. (If the user specifies
445 * that SIGINT is to be trapped or ignored using the trap builtin, then
446 * this routine is not called.) Suppressint is nonzero when interrupts
447 * are held using the INT_OFF macro. (The test for iflag is just
448 * defensive programming.)
449 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000450static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000451static void
452raise_interrupt(void)
453{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000454 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000455
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200456 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000457 /* Signal is not automatically unmasked after it is raised,
458 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000459 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200460 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000461
Denis Vlasenko4b875702009-03-19 13:30:04 +0000462 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000463 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
464 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000465 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000466 signal(SIGINT, SIG_DFL);
467 raise(SIGINT);
468 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000469 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000470 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000471 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000472 /* NOTREACHED */
473}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000474#if DEBUG
475#define raise_interrupt() do { \
476 TRACE(("raising interrupt on line %d\n", __LINE__)); \
477 raise_interrupt(); \
478} while (0)
479#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000480
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000481static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000482int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000483{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000484 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200485 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000486 raise_interrupt();
487 }
488}
489#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000490static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000491force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000492{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000493 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200494 suppress_int = 0;
495 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000496 raise_interrupt();
497}
498#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000499
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200500#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000501
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000502#define RESTORE_INT(v) do { \
503 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200504 suppress_int = (v); \
505 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000506 raise_interrupt(); \
507} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000509
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000510/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000511
Eric Andersenc470f442003-07-28 09:56:35 +0000512static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000513outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000514{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000515 INT_OFF;
516 fputs(p, file);
517 INT_ON;
518}
519
520static void
521flush_stdout_stderr(void)
522{
523 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100524 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000525 INT_ON;
526}
527
528static void
529outcslow(int c, FILE *dest)
530{
531 INT_OFF;
532 putc(c, dest);
533 fflush(dest);
534 INT_ON;
535}
536
537static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
538static int
539out1fmt(const char *fmt, ...)
540{
541 va_list ap;
542 int r;
543
544 INT_OFF;
545 va_start(ap, fmt);
546 r = vprintf(fmt, ap);
547 va_end(ap);
548 INT_ON;
549 return r;
550}
551
552static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
553static int
554fmtstr(char *outbuf, size_t length, const char *fmt, ...)
555{
556 va_list ap;
557 int ret;
558
559 va_start(ap, fmt);
560 INT_OFF;
561 ret = vsnprintf(outbuf, length, fmt, ap);
562 va_end(ap);
563 INT_ON;
564 return ret;
565}
566
567static void
568out1str(const char *p)
569{
570 outstr(p, stdout);
571}
572
573static void
574out2str(const char *p)
575{
576 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100577 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000578}
579
580
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000581/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000582
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000583/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100584#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200585#define CTLESC ((unsigned char)'\201') /* escape next character */
586#define CTLVAR ((unsigned char)'\202') /* variable defn */
587#define CTLENDVAR ((unsigned char)'\203')
588#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000589#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
590/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200591#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
592#define CTLENDARI ((unsigned char)'\207')
593#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100594#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000595
596/* variable substitution byte (follows CTLVAR) */
597#define VSTYPE 0x0f /* type of variable substitution */
598#define VSNUL 0x10 /* colon--treat the empty string as unset */
599#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
600
601/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000602#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
603#define VSMINUS 0x2 /* ${var-text} */
604#define VSPLUS 0x3 /* ${var+text} */
605#define VSQUESTION 0x4 /* ${var?message} */
606#define VSASSIGN 0x5 /* ${var=text} */
607#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
608#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
609#define VSTRIMLEFT 0x8 /* ${var#pattern} */
610#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
611#define VSLENGTH 0xa /* ${#var} */
612#if ENABLE_ASH_BASH_COMPAT
613#define VSSUBSTR 0xc /* ${var:position:length} */
614#define VSREPLACE 0xd /* ${var/pattern/replacement} */
615#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
616#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000617
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000618static const char dolatstr[] ALIGN1 = {
619 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
620};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000621
Denis Vlasenko559691a2008-10-05 18:39:31 +0000622#define NCMD 0
623#define NPIPE 1
624#define NREDIR 2
625#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000626#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000627#define NAND 5
628#define NOR 6
629#define NSEMI 7
630#define NIF 8
631#define NWHILE 9
632#define NUNTIL 10
633#define NFOR 11
634#define NCASE 12
635#define NCLIST 13
636#define NDEFUN 14
637#define NARG 15
638#define NTO 16
639#if ENABLE_ASH_BASH_COMPAT
640#define NTO2 17
641#endif
642#define NCLOBBER 18
643#define NFROM 19
644#define NFROMTO 20
645#define NAPPEND 21
646#define NTOFD 22
647#define NFROMFD 23
648#define NHERE 24
649#define NXHERE 25
650#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000651#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000652
653union node;
654
655struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000656 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000657 union node *assign;
658 union node *args;
659 union node *redirect;
660};
661
662struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000663 smallint type;
664 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000665 struct nodelist *cmdlist;
666};
667
668struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000669 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000670 union node *n;
671 union node *redirect;
672};
673
674struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000675 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000676 union node *ch1;
677 union node *ch2;
678};
679
680struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000681 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000682 union node *test;
683 union node *ifpart;
684 union node *elsepart;
685};
686
687struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000688 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000689 union node *args;
690 union node *body;
691 char *var;
692};
693
694struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000695 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000696 union node *expr;
697 union node *cases;
698};
699
700struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000701 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000702 union node *next;
703 union node *pattern;
704 union node *body;
705};
706
707struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000708 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000709 union node *next;
710 char *text;
711 struct nodelist *backquote;
712};
713
Denis Vlasenko559691a2008-10-05 18:39:31 +0000714/* nfile and ndup layout must match!
715 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
716 * that it is actually NTO2 (>&file), and change its type.
717 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000719 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000720 union node *next;
721 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000722 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000723 union node *fname;
724 char *expfname;
725};
726
727struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000728 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000729 union node *next;
730 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000731 int dupfd;
732 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000733 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000734};
735
736struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000737 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000738 union node *next;
739 int fd;
740 union node *doc;
741};
742
743struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000744 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000745 union node *com;
746};
747
748union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000749 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000750 struct ncmd ncmd;
751 struct npipe npipe;
752 struct nredir nredir;
753 struct nbinary nbinary;
754 struct nif nif;
755 struct nfor nfor;
756 struct ncase ncase;
757 struct nclist nclist;
758 struct narg narg;
759 struct nfile nfile;
760 struct ndup ndup;
761 struct nhere nhere;
762 struct nnot nnot;
763};
764
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200765/*
766 * NODE_EOF is returned by parsecmd when it encounters an end of file.
767 * It must be distinct from NULL.
768 */
769#define NODE_EOF ((union node *) -1L)
770
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000771struct nodelist {
772 struct nodelist *next;
773 union node *n;
774};
775
776struct funcnode {
777 int count;
778 union node n;
779};
780
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000781/*
782 * Free a parse tree.
783 */
784static void
785freefunc(struct funcnode *f)
786{
787 if (f && --f->count < 0)
788 free(f);
789}
790
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000791
792/* ============ Debugging output */
793
794#if DEBUG
795
796static FILE *tracefile;
797
798static void
799trace_printf(const char *fmt, ...)
800{
801 va_list va;
802
803 if (debug != 1)
804 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000805 if (DEBUG_TIME)
806 fprintf(tracefile, "%u ", (int) time(NULL));
807 if (DEBUG_PID)
808 fprintf(tracefile, "[%u] ", (int) getpid());
809 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200810 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000811 va_start(va, fmt);
812 vfprintf(tracefile, fmt, va);
813 va_end(va);
814}
815
816static void
817trace_vprintf(const char *fmt, va_list va)
818{
819 if (debug != 1)
820 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000821 if (DEBUG_TIME)
822 fprintf(tracefile, "%u ", (int) time(NULL));
823 if (DEBUG_PID)
824 fprintf(tracefile, "[%u] ", (int) getpid());
825 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200826 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000827 vfprintf(tracefile, fmt, va);
828}
829
830static void
831trace_puts(const char *s)
832{
833 if (debug != 1)
834 return;
835 fputs(s, tracefile);
836}
837
838static void
839trace_puts_quoted(char *s)
840{
841 char *p;
842 char c;
843
844 if (debug != 1)
845 return;
846 putc('"', tracefile);
847 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100848 switch ((unsigned char)*p) {
849 case '\n': c = 'n'; goto backslash;
850 case '\t': c = 't'; goto backslash;
851 case '\r': c = 'r'; goto backslash;
852 case '\"': c = '\"'; goto backslash;
853 case '\\': c = '\\'; goto backslash;
854 case CTLESC: c = 'e'; goto backslash;
855 case CTLVAR: c = 'v'; goto backslash;
856 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
857 case CTLBACKQ: c = 'q'; goto backslash;
858 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000859 backslash:
860 putc('\\', tracefile);
861 putc(c, tracefile);
862 break;
863 default:
864 if (*p >= ' ' && *p <= '~')
865 putc(*p, tracefile);
866 else {
867 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100868 putc((*p >> 6) & 03, tracefile);
869 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000870 putc(*p & 07, tracefile);
871 }
872 break;
873 }
874 }
875 putc('"', tracefile);
876}
877
878static void
879trace_puts_args(char **ap)
880{
881 if (debug != 1)
882 return;
883 if (!*ap)
884 return;
885 while (1) {
886 trace_puts_quoted(*ap);
887 if (!*++ap) {
888 putc('\n', tracefile);
889 break;
890 }
891 putc(' ', tracefile);
892 }
893}
894
895static void
896opentrace(void)
897{
898 char s[100];
899#ifdef O_APPEND
900 int flags;
901#endif
902
903 if (debug != 1) {
904 if (tracefile)
905 fflush(tracefile);
906 /* leave open because libedit might be using it */
907 return;
908 }
909 strcpy(s, "./trace");
910 if (tracefile) {
911 if (!freopen(s, "a", tracefile)) {
912 fprintf(stderr, "Can't re-open %s\n", s);
913 debug = 0;
914 return;
915 }
916 } else {
917 tracefile = fopen(s, "a");
918 if (tracefile == NULL) {
919 fprintf(stderr, "Can't open %s\n", s);
920 debug = 0;
921 return;
922 }
923 }
924#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000925 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000926 if (flags >= 0)
927 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
928#endif
929 setlinebuf(tracefile);
930 fputs("\nTracing started.\n", tracefile);
931}
932
933static void
934indent(int amount, char *pfx, FILE *fp)
935{
936 int i;
937
938 for (i = 0; i < amount; i++) {
939 if (pfx && i == amount - 1)
940 fputs(pfx, fp);
941 putc('\t', fp);
942 }
943}
944
945/* little circular references here... */
946static void shtree(union node *n, int ind, char *pfx, FILE *fp);
947
948static void
949sharg(union node *arg, FILE *fp)
950{
951 char *p;
952 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100953 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954
955 if (arg->type != NARG) {
956 out1fmt("<node type %d>\n", arg->type);
957 abort();
958 }
959 bqlist = arg->narg.backquote;
960 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100961 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000962 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700963 p++;
964 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000965 break;
966 case CTLVAR:
967 putc('$', fp);
968 putc('{', fp);
969 subtype = *++p;
970 if (subtype == VSLENGTH)
971 putc('#', fp);
972
Dan Fandrich77d48722010-09-07 23:38:28 -0700973 while (*p != '=') {
974 putc(*p, fp);
975 p++;
976 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000977
978 if (subtype & VSNUL)
979 putc(':', fp);
980
981 switch (subtype & VSTYPE) {
982 case VSNORMAL:
983 putc('}', fp);
984 break;
985 case VSMINUS:
986 putc('-', fp);
987 break;
988 case VSPLUS:
989 putc('+', fp);
990 break;
991 case VSQUESTION:
992 putc('?', fp);
993 break;
994 case VSASSIGN:
995 putc('=', fp);
996 break;
997 case VSTRIMLEFT:
998 putc('#', fp);
999 break;
1000 case VSTRIMLEFTMAX:
1001 putc('#', fp);
1002 putc('#', fp);
1003 break;
1004 case VSTRIMRIGHT:
1005 putc('%', fp);
1006 break;
1007 case VSTRIMRIGHTMAX:
1008 putc('%', fp);
1009 putc('%', fp);
1010 break;
1011 case VSLENGTH:
1012 break;
1013 default:
1014 out1fmt("<subtype %d>", subtype);
1015 }
1016 break;
1017 case CTLENDVAR:
1018 putc('}', fp);
1019 break;
1020 case CTLBACKQ:
1021 case CTLBACKQ|CTLQUOTE:
1022 putc('$', fp);
1023 putc('(', fp);
1024 shtree(bqlist->n, -1, NULL, fp);
1025 putc(')', fp);
1026 break;
1027 default:
1028 putc(*p, fp);
1029 break;
1030 }
1031 }
1032}
1033
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001034static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001035shcmd(union node *cmd, FILE *fp)
1036{
1037 union node *np;
1038 int first;
1039 const char *s;
1040 int dftfd;
1041
1042 first = 1;
1043 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001044 if (!first)
1045 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001046 sharg(np, fp);
1047 first = 0;
1048 }
1049 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001050 if (!first)
1051 putc(' ', fp);
1052 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001053 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001054 case NTO: s = ">>"+1; dftfd = 1; break;
1055 case NCLOBBER: s = ">|"; dftfd = 1; break;
1056 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001057#if ENABLE_ASH_BASH_COMPAT
1058 case NTO2:
1059#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001060 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001061 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001062 case NFROMFD: s = "<&"; break;
1063 case NFROMTO: s = "<>"; break;
1064 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001065 }
1066 if (np->nfile.fd != dftfd)
1067 fprintf(fp, "%d", np->nfile.fd);
1068 fputs(s, fp);
1069 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1070 fprintf(fp, "%d", np->ndup.dupfd);
1071 } else {
1072 sharg(np->nfile.fname, fp);
1073 }
1074 first = 0;
1075 }
1076}
1077
1078static void
1079shtree(union node *n, int ind, char *pfx, FILE *fp)
1080{
1081 struct nodelist *lp;
1082 const char *s;
1083
1084 if (n == NULL)
1085 return;
1086
1087 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001088
1089 if (n == NODE_EOF) {
1090 fputs("<EOF>", fp);
1091 return;
1092 }
1093
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001094 switch (n->type) {
1095 case NSEMI:
1096 s = "; ";
1097 goto binop;
1098 case NAND:
1099 s = " && ";
1100 goto binop;
1101 case NOR:
1102 s = " || ";
1103 binop:
1104 shtree(n->nbinary.ch1, ind, NULL, fp);
1105 /* if (ind < 0) */
1106 fputs(s, fp);
1107 shtree(n->nbinary.ch2, ind, NULL, fp);
1108 break;
1109 case NCMD:
1110 shcmd(n, fp);
1111 if (ind >= 0)
1112 putc('\n', fp);
1113 break;
1114 case NPIPE:
1115 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001116 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001117 if (lp->next)
1118 fputs(" | ", fp);
1119 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001120 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001121 fputs(" &", fp);
1122 if (ind >= 0)
1123 putc('\n', fp);
1124 break;
1125 default:
1126 fprintf(fp, "<node type %d>", n->type);
1127 if (ind >= 0)
1128 putc('\n', fp);
1129 break;
1130 }
1131}
1132
1133static void
1134showtree(union node *n)
1135{
1136 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001137 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001138}
1139
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001140#endif /* DEBUG */
1141
1142
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001143/* ============ Parser data */
1144
1145/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001146 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1147 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001148struct strlist {
1149 struct strlist *next;
1150 char *text;
1151};
1152
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001153struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001154
Denis Vlasenkob012b102007-02-19 22:43:01 +00001155struct strpush {
1156 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001157 char *prev_string;
1158 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001159#if ENABLE_ASH_ALIAS
1160 struct alias *ap; /* if push was associated with an alias */
1161#endif
1162 char *string; /* remember the string since it may change */
1163};
1164
1165struct parsefile {
1166 struct parsefile *prev; /* preceding file on stack */
1167 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001168 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001169 int left_in_line; /* number of chars left in this line */
1170 int left_in_buffer; /* number of chars left in this buffer past the line */
1171 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001172 char *buf; /* input buffer */
1173 struct strpush *strpush; /* for pushing strings at this level */
1174 struct strpush basestrpush; /* so pushing one is fast */
1175};
1176
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001177static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001178static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001179static int startlinno; /* line # where last token started */
1180static char *commandname; /* currently executing command */
1181static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001182static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001183
1184
1185/* ============ Message printing */
1186
1187static void
1188ash_vmsg(const char *msg, va_list ap)
1189{
1190 fprintf(stderr, "%s: ", arg0);
1191 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001192 if (strcmp(arg0, commandname))
1193 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001194 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001195 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001196 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001197 vfprintf(stderr, msg, ap);
1198 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001199}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001200
1201/*
1202 * Exverror is called to raise the error exception. If the second argument
1203 * is not NULL then error prints an error message using printf style
1204 * formatting. It then raises the error exception.
1205 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001206static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001207static void
1208ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001209{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001210#if DEBUG
1211 if (msg) {
1212 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1213 TRACEV((msg, ap));
1214 TRACE(("\") pid=%d\n", getpid()));
1215 } else
1216 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1217 if (msg)
1218#endif
1219 ash_vmsg(msg, ap);
1220
1221 flush_stdout_stderr();
1222 raise_exception(cond);
1223 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001224}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001225
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001226static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001227static void
1228ash_msg_and_raise_error(const char *msg, ...)
1229{
1230 va_list ap;
1231
1232 va_start(ap, msg);
1233 ash_vmsg_and_raise(EXERROR, msg, ap);
1234 /* NOTREACHED */
1235 va_end(ap);
1236}
1237
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001238static void raise_error_syntax(const char *) NORETURN;
1239static void
1240raise_error_syntax(const char *msg)
1241{
1242 ash_msg_and_raise_error("syntax error: %s", msg);
1243 /* NOTREACHED */
1244}
1245
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001246static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001247static void
1248ash_msg_and_raise(int cond, const char *msg, ...)
1249{
1250 va_list ap;
1251
1252 va_start(ap, msg);
1253 ash_vmsg_and_raise(cond, msg, ap);
1254 /* NOTREACHED */
1255 va_end(ap);
1256}
1257
1258/*
1259 * error/warning routines for external builtins
1260 */
1261static void
1262ash_msg(const char *fmt, ...)
1263{
1264 va_list ap;
1265
1266 va_start(ap, fmt);
1267 ash_vmsg(fmt, ap);
1268 va_end(ap);
1269}
1270
1271/*
1272 * Return a string describing an error. The returned string may be a
1273 * pointer to a static buffer that will be overwritten on the next call.
1274 * Action describes the operation that got the error.
1275 */
1276static const char *
1277errmsg(int e, const char *em)
1278{
1279 if (e == ENOENT || e == ENOTDIR) {
1280 return em;
1281 }
1282 return strerror(e);
1283}
1284
1285
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001286/* ============ Memory allocation */
1287
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001288#if 0
1289/* I consider these wrappers nearly useless:
1290 * ok, they return you to nearest exception handler, but
1291 * how much memory do you leak in the process, making
1292 * memory starvation worse?
1293 */
1294static void *
1295ckrealloc(void * p, size_t nbytes)
1296{
1297 p = realloc(p, nbytes);
1298 if (!p)
Tanguy Pruvot6d9e2a32011-07-04 05:52:52 +02001299 ash_msg_and_raise_error("%s", bb_msg_memory_exhausted);
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001300 return p;
1301}
1302
1303static void *
1304ckmalloc(size_t nbytes)
1305{
1306 return ckrealloc(NULL, nbytes);
1307}
1308
1309static void *
1310ckzalloc(size_t nbytes)
1311{
1312 return memset(ckmalloc(nbytes), 0, nbytes);
1313}
1314
1315static char *
1316ckstrdup(const char *s)
1317{
1318 char *p = strdup(s);
1319 if (!p)
Tanguy Pruvot6d9e2a32011-07-04 05:52:52 +02001320 ash_msg_and_raise_error("%s", bb_msg_memory_exhausted);
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001321 return p;
1322}
1323#else
1324/* Using bbox equivalents. They exit if out of memory */
1325# define ckrealloc xrealloc
1326# define ckmalloc xmalloc
1327# define ckzalloc xzalloc
1328# define ckstrdup xstrdup
1329#endif
1330
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001331/*
1332 * It appears that grabstackstr() will barf with such alignments
1333 * because stalloc() will return a string allocated in a new stackblock.
1334 */
1335#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1336enum {
1337 /* Most machines require the value returned from malloc to be aligned
1338 * in some way. The following macro will get this right
1339 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001340 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001341 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001342 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001343};
1344
1345struct stack_block {
1346 struct stack_block *prev;
1347 char space[MINSIZE];
1348};
1349
1350struct stackmark {
1351 struct stack_block *stackp;
1352 char *stacknxt;
1353 size_t stacknleft;
1354 struct stackmark *marknext;
1355};
1356
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001357
Denis Vlasenko01631112007-12-16 17:20:38 +00001358struct globals_memstack {
1359 struct stack_block *g_stackp; // = &stackbase;
1360 struct stackmark *markp;
1361 char *g_stacknxt; // = stackbase.space;
1362 char *sstrend; // = stackbase.space + MINSIZE;
1363 size_t g_stacknleft; // = MINSIZE;
1364 int herefd; // = -1;
1365 struct stack_block stackbase;
1366};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001367extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1368#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001369#define g_stackp (G_memstack.g_stackp )
1370#define markp (G_memstack.markp )
1371#define g_stacknxt (G_memstack.g_stacknxt )
1372#define sstrend (G_memstack.sstrend )
1373#define g_stacknleft (G_memstack.g_stacknleft)
1374#define herefd (G_memstack.herefd )
1375#define stackbase (G_memstack.stackbase )
1376#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001377 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1378 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001379 g_stackp = &stackbase; \
1380 g_stacknxt = stackbase.space; \
1381 g_stacknleft = MINSIZE; \
1382 sstrend = stackbase.space + MINSIZE; \
1383 herefd = -1; \
1384} while (0)
1385
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001386
Denis Vlasenko01631112007-12-16 17:20:38 +00001387#define stackblock() ((void *)g_stacknxt)
1388#define stackblocksize() g_stacknleft
1389
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001390/*
1391 * Parse trees for commands are allocated in lifo order, so we use a stack
1392 * to make this more efficient, and also to avoid all sorts of exception
1393 * handling code to handle interrupts in the middle of a parse.
1394 *
1395 * The size 504 was chosen because the Ultrix malloc handles that size
1396 * well.
1397 */
1398static void *
1399stalloc(size_t nbytes)
1400{
1401 char *p;
1402 size_t aligned;
1403
1404 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001405 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001406 size_t len;
1407 size_t blocksize;
1408 struct stack_block *sp;
1409
1410 blocksize = aligned;
1411 if (blocksize < MINSIZE)
1412 blocksize = MINSIZE;
1413 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1414 if (len < blocksize)
Tanguy Pruvot6d9e2a32011-07-04 05:52:52 +02001415 ash_msg_and_raise_error("%s", bb_msg_memory_exhausted);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001416 INT_OFF;
1417 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001418 sp->prev = g_stackp;
1419 g_stacknxt = sp->space;
1420 g_stacknleft = blocksize;
1421 sstrend = g_stacknxt + blocksize;
1422 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423 INT_ON;
1424 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001425 p = g_stacknxt;
1426 g_stacknxt += aligned;
1427 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001428 return p;
1429}
1430
Denis Vlasenko597906c2008-02-20 16:38:54 +00001431static void *
1432stzalloc(size_t nbytes)
1433{
1434 return memset(stalloc(nbytes), 0, nbytes);
1435}
1436
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001437static void
1438stunalloc(void *p)
1439{
1440#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001441 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001442 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001443 abort();
1444 }
1445#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001446 g_stacknleft += g_stacknxt - (char *)p;
1447 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001448}
1449
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001450/*
1451 * Like strdup but works with the ash stack.
1452 */
1453static char *
1454ststrdup(const char *p)
1455{
1456 size_t len = strlen(p) + 1;
1457 return memcpy(stalloc(len), p, len);
1458}
1459
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001460static void
1461setstackmark(struct stackmark *mark)
1462{
Denis Vlasenko01631112007-12-16 17:20:38 +00001463 mark->stackp = g_stackp;
1464 mark->stacknxt = g_stacknxt;
1465 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001466 mark->marknext = markp;
1467 markp = mark;
1468}
1469
1470static void
1471popstackmark(struct stackmark *mark)
1472{
1473 struct stack_block *sp;
1474
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001475 if (!mark->stackp)
1476 return;
1477
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001478 INT_OFF;
1479 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001480 while (g_stackp != mark->stackp) {
1481 sp = g_stackp;
1482 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001483 free(sp);
1484 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001485 g_stacknxt = mark->stacknxt;
1486 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001487 sstrend = mark->stacknxt + mark->stacknleft;
1488 INT_ON;
1489}
1490
1491/*
1492 * When the parser reads in a string, it wants to stick the string on the
1493 * stack and only adjust the stack pointer when it knows how big the
1494 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1495 * of space on top of the stack and stackblocklen returns the length of
1496 * this block. Growstackblock will grow this space by at least one byte,
1497 * possibly moving it (like realloc). Grabstackblock actually allocates the
1498 * part of the block that has been used.
1499 */
1500static void
1501growstackblock(void)
1502{
1503 size_t newlen;
1504
Denis Vlasenko01631112007-12-16 17:20:38 +00001505 newlen = g_stacknleft * 2;
1506 if (newlen < g_stacknleft)
Tanguy Pruvot6d9e2a32011-07-04 05:52:52 +02001507 ash_msg_and_raise_error("%s", bb_msg_memory_exhausted);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001508 if (newlen < 128)
1509 newlen += 128;
1510
Denis Vlasenko01631112007-12-16 17:20:38 +00001511 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001512 struct stack_block *oldstackp;
1513 struct stackmark *xmark;
1514 struct stack_block *sp;
1515 struct stack_block *prevstackp;
1516 size_t grosslen;
1517
1518 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001519 oldstackp = g_stackp;
1520 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001521 prevstackp = sp->prev;
1522 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1523 sp = ckrealloc(sp, grosslen);
1524 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001525 g_stackp = sp;
1526 g_stacknxt = sp->space;
1527 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001528 sstrend = sp->space + newlen;
1529
1530 /*
1531 * Stack marks pointing to the start of the old block
1532 * must be relocated to point to the new block
1533 */
1534 xmark = markp;
1535 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001536 xmark->stackp = g_stackp;
1537 xmark->stacknxt = g_stacknxt;
1538 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001539 xmark = xmark->marknext;
1540 }
1541 INT_ON;
1542 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001543 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001544 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001545 char *p = stalloc(newlen);
1546
1547 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001548 g_stacknxt = memcpy(p, oldspace, oldlen);
1549 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001550 }
1551}
1552
1553static void
1554grabstackblock(size_t len)
1555{
1556 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001557 g_stacknxt += len;
1558 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001559}
1560
1561/*
1562 * The following routines are somewhat easier to use than the above.
1563 * The user declares a variable of type STACKSTR, which may be declared
1564 * to be a register. The macro STARTSTACKSTR initializes things. Then
1565 * the user uses the macro STPUTC to add characters to the string. In
1566 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1567 * grown as necessary. When the user is done, she can just leave the
1568 * string there and refer to it using stackblock(). Or she can allocate
1569 * the space for it using grabstackstr(). If it is necessary to allow
1570 * someone else to use the stack temporarily and then continue to grow
1571 * the string, the user should use grabstack to allocate the space, and
1572 * then call ungrabstr(p) to return to the previous mode of operation.
1573 *
1574 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1575 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1576 * is space for at least one character.
1577 */
1578static void *
1579growstackstr(void)
1580{
1581 size_t len = stackblocksize();
1582 if (herefd >= 0 && len >= 1024) {
1583 full_write(herefd, stackblock(), len);
1584 return stackblock();
1585 }
1586 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001587 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001588}
1589
1590/*
1591 * Called from CHECKSTRSPACE.
1592 */
1593static char *
1594makestrspace(size_t newlen, char *p)
1595{
Denis Vlasenko01631112007-12-16 17:20:38 +00001596 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001597 size_t size = stackblocksize();
1598
1599 for (;;) {
1600 size_t nleft;
1601
1602 size = stackblocksize();
1603 nleft = size - len;
1604 if (nleft >= newlen)
1605 break;
1606 growstackblock();
1607 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001608 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001609}
1610
1611static char *
1612stack_nputstr(const char *s, size_t n, char *p)
1613{
1614 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001615 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001616 return p;
1617}
1618
1619static char *
1620stack_putstr(const char *s, char *p)
1621{
1622 return stack_nputstr(s, strlen(s), p);
1623}
1624
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001625static char *
1626_STPUTC(int c, char *p)
1627{
1628 if (p == sstrend)
1629 p = growstackstr();
1630 *p++ = c;
1631 return p;
1632}
1633
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001634#define STARTSTACKSTR(p) ((p) = stackblock())
1635#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001636#define CHECKSTRSPACE(n, p) do { \
1637 char *q = (p); \
1638 size_t l = (n); \
1639 size_t m = sstrend - q; \
1640 if (l > m) \
1641 (p) = makestrspace(l, q); \
1642} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001643#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001644#define STACKSTRNUL(p) do { \
1645 if ((p) == sstrend) \
1646 (p) = growstackstr(); \
1647 *(p) = '\0'; \
1648} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001649#define STUNPUTC(p) (--(p))
1650#define STTOPC(p) ((p)[-1])
1651#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001652
1653#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001654#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655#define stackstrend() ((void *)sstrend)
1656
1657
1658/* ============ String helpers */
1659
1660/*
1661 * prefix -- see if pfx is a prefix of string.
1662 */
1663static char *
1664prefix(const char *string, const char *pfx)
1665{
1666 while (*pfx) {
1667 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001668 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001669 }
1670 return (char *) string;
1671}
1672
1673/*
1674 * Check for a valid number. This should be elsewhere.
1675 */
1676static int
1677is_number(const char *p)
1678{
1679 do {
1680 if (!isdigit(*p))
1681 return 0;
1682 } while (*++p != '\0');
1683 return 1;
1684}
1685
1686/*
1687 * Convert a string of digits to an integer, printing an error message on
1688 * failure.
1689 */
1690static int
1691number(const char *s)
1692{
1693 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001694 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001695 return atoi(s);
1696}
1697
1698/*
1699 * Produce a possibly single quoted string suitable as input to the shell.
1700 * The return string is allocated on the stack.
1701 */
1702static char *
1703single_quote(const char *s)
1704{
1705 char *p;
1706
1707 STARTSTACKSTR(p);
1708
1709 do {
1710 char *q;
1711 size_t len;
1712
1713 len = strchrnul(s, '\'') - s;
1714
1715 q = p = makestrspace(len + 3, p);
1716
1717 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001718 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001719 *q++ = '\'';
1720 s += len;
1721
1722 STADJUST(q - p, p);
1723
Denys Vlasenkocd716832009-11-28 22:14:02 +01001724 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001725 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001726 len = 0;
1727 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728
1729 q = p = makestrspace(len + 3, p);
1730
1731 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001732 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001733 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734
1735 STADJUST(q - p, p);
1736 } while (*s);
1737
Denys Vlasenkocd716832009-11-28 22:14:02 +01001738 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001739
1740 return stackblock();
1741}
1742
1743
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001744/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001745
1746static char **argptr; /* argument list for builtin commands */
1747static char *optionarg; /* set by nextopt (like getopt) */
1748static char *optptr; /* used by nextopt */
1749
1750/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001751 * XXX - should get rid of. Have all builtins use getopt(3).
1752 * The library getopt must have the BSD extension static variable
1753 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001755 * Standard option processing (a la getopt) for builtin routines.
1756 * The only argument that is passed to nextopt is the option string;
1757 * the other arguments are unnecessary. It returns the character,
1758 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001759 */
1760static int
1761nextopt(const char *optstring)
1762{
1763 char *p;
1764 const char *q;
1765 char c;
1766
1767 p = optptr;
1768 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001769 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001770 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001771 if (p == NULL)
1772 return '\0';
1773 if (*p != '-')
1774 return '\0';
1775 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 return '\0';
1777 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001778 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001779 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001780 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001781 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001782 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001783 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001784 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001785 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001786 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001787 if (*++q == ':')
1788 q++;
1789 }
1790 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001791 if (*p == '\0') {
1792 p = *argptr++;
1793 if (p == NULL)
1794 ash_msg_and_raise_error("no arg for -%c option", c);
1795 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001796 optionarg = p;
1797 p = NULL;
1798 }
1799 optptr = p;
1800 return c;
1801}
1802
1803
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001804/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001805
Denis Vlasenko01631112007-12-16 17:20:38 +00001806/*
1807 * The parsefile structure pointed to by the global variable parsefile
1808 * contains information about the current file being read.
1809 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001810struct shparam {
1811 int nparam; /* # of positional parameters (without $0) */
1812#if ENABLE_ASH_GETOPTS
1813 int optind; /* next parameter to be processed by getopts */
1814 int optoff; /* used by getopts */
1815#endif
1816 unsigned char malloced; /* if parameter list dynamically allocated */
1817 char **p; /* parameter list */
1818};
1819
1820/*
1821 * Free the list of positional parameters.
1822 */
1823static void
1824freeparam(volatile struct shparam *param)
1825{
Denis Vlasenko01631112007-12-16 17:20:38 +00001826 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001827 char **ap, **ap1;
1828 ap = ap1 = param->p;
1829 while (*ap)
1830 free(*ap++);
1831 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001832 }
1833}
1834
1835#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001836static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001837#endif
1838
1839struct var {
1840 struct var *next; /* next entry in hash list */
1841 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001842 const char *var_text; /* name=value */
1843 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001844 /* the variable gets set/unset */
1845};
1846
1847struct localvar {
1848 struct localvar *next; /* next local variable in list */
1849 struct var *vp; /* the variable that was made local */
1850 int flags; /* saved flags */
1851 const char *text; /* saved text */
1852};
1853
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001854/* flags */
1855#define VEXPORT 0x01 /* variable is exported */
1856#define VREADONLY 0x02 /* variable cannot be modified */
1857#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1858#define VTEXTFIXED 0x08 /* text is statically allocated */
1859#define VSTACK 0x10 /* text is allocated on the stack */
1860#define VUNSET 0x20 /* the variable is not set */
1861#define VNOFUNC 0x40 /* don't call the callback function */
1862#define VNOSET 0x80 /* do not set variable - just readonly test */
1863#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001864#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001865# define VDYNAMIC 0x200 /* dynamic variable */
1866#else
1867# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001868#endif
1869
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001870
Denis Vlasenko01631112007-12-16 17:20:38 +00001871/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001872#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001873static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001874change_lc_all(const char *value)
1875{
1876 if (value && *value != '\0')
1877 setlocale(LC_ALL, value);
1878}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001879static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001880change_lc_ctype(const char *value)
1881{
1882 if (value && *value != '\0')
1883 setlocale(LC_CTYPE, value);
1884}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001885#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001886#if ENABLE_ASH_MAIL
1887static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001888static void changemail(const char *var_value) FAST_FUNC;
1889#else
1890# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001891#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001892static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001894static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001895#endif
1896
Denis Vlasenko01631112007-12-16 17:20:38 +00001897static const struct {
1898 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001899 const char *var_text;
1900 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001901} varinit_data[] = {
Denys Vlasenko6e73af52012-07-14 01:07:39 +02001902 /*
1903 * Note: VEXPORT would not work correctly here for NOFORK applets:
1904 * some environment strings may be constant.
1905 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001906 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001907#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001908 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1909 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001910#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001911 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1912 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1913 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1914 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001915#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001916 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001917#endif
1918#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001919 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001920#endif
1921#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001922 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1923 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001924#endif
1925#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001926 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001927#endif
1928};
1929
Denis Vlasenko0b769642008-07-24 07:54:57 +00001930struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001931
1932struct globals_var {
1933 struct shparam shellparam; /* $@ current positional parameters */
1934 struct redirtab *redirlist;
1935 int g_nullredirs;
1936 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1937 struct var *vartab[VTABSIZE];
1938 struct var varinit[ARRAY_SIZE(varinit_data)];
1939};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001940extern struct globals_var *const ash_ptr_to_globals_var;
1941#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001942#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001943//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001944#define g_nullredirs (G_var.g_nullredirs )
1945#define preverrout_fd (G_var.preverrout_fd)
1946#define vartab (G_var.vartab )
1947#define varinit (G_var.varinit )
1948#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001949 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001950 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1951 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001952 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001953 varinit[i].flags = varinit_data[i].flags; \
1954 varinit[i].var_text = varinit_data[i].var_text; \
1955 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001956 } \
1957} while (0)
1958
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001959#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001960#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001961# define vmail (&vifs)[1]
1962# define vmpath (&vmail)[1]
1963# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001964#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001965# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001966#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001967#define vps1 (&vpath)[1]
1968#define vps2 (&vps1)[1]
1969#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001971# define voptind (&vps4)[1]
1972# if ENABLE_ASH_RANDOM_SUPPORT
1973# define vrandom (&voptind)[1]
1974# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001975#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001976# if ENABLE_ASH_RANDOM_SUPPORT
1977# define vrandom (&vps4)[1]
1978# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001979#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980
1981/*
1982 * The following macros access the values of the above variables.
1983 * They have to skip over the name. They return the null string
1984 * for unset variables.
1985 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001986#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001987#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001988#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001989# define mailval() (vmail.var_text + 5)
1990# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001991# define mpathset() ((vmpath.flags & VUNSET) == 0)
1992#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001993#define pathval() (vpath.var_text + 5)
1994#define ps1val() (vps1.var_text + 4)
1995#define ps2val() (vps2.var_text + 4)
1996#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001997#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001998# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001999#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002000
Denis Vlasenko01631112007-12-16 17:20:38 +00002001#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002002static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002003getoptsreset(const char *value)
2004{
2005 shellparam.optind = number(value);
2006 shellparam.optoff = -1;
2007}
2008#endif
2009
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002010/* math.h has these, otherwise define our private copies */
2011#if !ENABLE_SH_MATH_SUPPORT
2012#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2013#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002014/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002015 * Return the pointer to the first char which is not part of a legal variable name
2016 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002017 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002018static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002019endofname(const char *name)
2020{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002021 if (!is_name(*name))
2022 return name;
2023 while (*++name) {
2024 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002025 break;
2026 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002027 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002028}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002029#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002030
2031/*
2032 * Compares two strings up to the first = or '\0'. The first
2033 * string must be terminated by '='; the second may be terminated by
2034 * either '=' or '\0'.
2035 */
2036static int
2037varcmp(const char *p, const char *q)
2038{
2039 int c, d;
2040
2041 while ((c = *p) == (d = *q)) {
2042 if (!c || c == '=')
2043 goto out;
2044 p++;
2045 q++;
2046 }
2047 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002048 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002049 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002050 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002051 out:
2052 return c - d;
2053}
2054
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002055/*
2056 * Find the appropriate entry in the hash table from the name.
2057 */
2058static struct var **
2059hashvar(const char *p)
2060{
2061 unsigned hashval;
2062
2063 hashval = ((unsigned char) *p) << 4;
2064 while (*p && *p != '=')
2065 hashval += (unsigned char) *p++;
2066 return &vartab[hashval % VTABSIZE];
2067}
2068
2069static int
2070vpcmp(const void *a, const void *b)
2071{
2072 return varcmp(*(const char **)a, *(const char **)b);
2073}
2074
2075/*
2076 * This routine initializes the builtin variables.
2077 */
2078static void
2079initvar(void)
2080{
2081 struct var *vp;
2082 struct var *end;
2083 struct var **vpp;
2084
2085 /*
2086 * PS1 depends on uid
2087 */
2088#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002089 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002090#else
2091 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002092 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002093#endif
2094 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002095 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002096 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002097 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002098 vp->next = *vpp;
2099 *vpp = vp;
2100 } while (++vp < end);
2101}
2102
2103static struct var **
2104findvar(struct var **vpp, const char *name)
2105{
2106 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002107 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002108 break;
2109 }
2110 }
2111 return vpp;
2112}
2113
2114/*
2115 * Find the value of a variable. Returns NULL if not set.
2116 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002117static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002118lookupvar(const char *name)
2119{
2120 struct var *v;
2121
2122 v = *findvar(hashvar(name), name);
2123 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002124#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002125 /*
2126 * Dynamic variables are implemented roughly the same way they are
2127 * in bash. Namely, they're "special" so long as they aren't unset.
2128 * As soon as they're unset, they're no longer dynamic, and dynamic
2129 * lookup will no longer happen at that point. -- PFM.
2130 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002131 if (v->flags & VDYNAMIC)
2132 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002133#endif
2134 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002135 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002136 }
2137 return NULL;
2138}
2139
2140/*
2141 * Search the environment of a builtin command.
2142 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002143static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002144bltinlookup(const char *name)
2145{
2146 struct strlist *sp;
2147
2148 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002149 if (varcmp(sp->text, name) == 0)
2150 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002151 }
2152 return lookupvar(name);
2153}
2154
2155/*
2156 * Same as setvar except that the variable and value are passed in
2157 * the first argument as name=value. Since the first argument will
2158 * be actually stored in the table, it should not be a string that
2159 * will go away.
2160 * Called with interrupts off.
2161 */
2162static void
2163setvareq(char *s, int flags)
2164{
2165 struct var *vp, **vpp;
2166
2167 vpp = hashvar(s);
2168 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2169 vp = *findvar(vpp, s);
2170 if (vp) {
2171 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2172 const char *n;
2173
2174 if (flags & VNOSAVE)
2175 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002176 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002177 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2178 }
2179
2180 if (flags & VNOSET)
2181 return;
2182
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002183 if (vp->var_func && !(flags & VNOFUNC))
2184 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002185
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002186 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2187 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002188
2189 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2190 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002191 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002192 if (flags & VNOSET)
2193 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002194 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002195 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002196 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002197 *vpp = vp;
2198 }
2199 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2200 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002201 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002202 vp->flags = flags;
2203}
2204
2205/*
2206 * Set the value of a variable. The flags argument is ored with the
2207 * flags of the variable. If val is NULL, the variable is unset.
2208 */
2209static void
2210setvar(const char *name, const char *val, int flags)
2211{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002212 const char *q;
2213 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002214 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002215 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002216 size_t vallen;
2217
2218 q = endofname(name);
2219 p = strchrnul(q, '=');
2220 namelen = p - name;
2221 if (!namelen || p != q)
2222 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2223 vallen = 0;
2224 if (val == NULL) {
2225 flags |= VUNSET;
2226 } else {
2227 vallen = strlen(val);
2228 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002229
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002230 INT_OFF;
2231 nameeq = ckmalloc(namelen + vallen + 2);
Tanguy Pruvot6fef6a32012-05-05 15:26:43 +02002232 p = (char*) ((uint32_t) memcpy(nameeq, name, namelen) + namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002233 if (val) {
2234 *p++ = '=';
Tanguy Pruvot6fef6a32012-05-05 15:26:43 +02002235 p = (char*) ((uint32_t) memcpy(p, val, vallen) + vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002236 }
2237 *p = '\0';
2238 setvareq(nameeq, flags | VNOSAVE);
2239 INT_ON;
2240}
2241
Denys Vlasenko03dad222010-01-12 23:29:57 +01002242static void FAST_FUNC
2243setvar2(const char *name, const char *val)
2244{
2245 setvar(name, val, 0);
2246}
2247
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002248#if ENABLE_ASH_GETOPTS
2249/*
2250 * Safe version of setvar, returns 1 on success 0 on failure.
2251 */
2252static int
2253setvarsafe(const char *name, const char *val, int flags)
2254{
2255 int err;
2256 volatile int saveint;
2257 struct jmploc *volatile savehandler = exception_handler;
2258 struct jmploc jmploc;
2259
2260 SAVE_INT(saveint);
2261 if (setjmp(jmploc.loc))
2262 err = 1;
2263 else {
2264 exception_handler = &jmploc;
2265 setvar(name, val, flags);
2266 err = 0;
2267 }
2268 exception_handler = savehandler;
2269 RESTORE_INT(saveint);
2270 return err;
2271}
2272#endif
2273
2274/*
2275 * Unset the specified variable.
2276 */
2277static int
2278unsetvar(const char *s)
2279{
2280 struct var **vpp;
2281 struct var *vp;
2282 int retval;
2283
2284 vpp = findvar(hashvar(s), s);
2285 vp = *vpp;
2286 retval = 2;
2287 if (vp) {
2288 int flags = vp->flags;
2289
2290 retval = 1;
2291 if (flags & VREADONLY)
2292 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002293#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002294 vp->flags &= ~VDYNAMIC;
2295#endif
2296 if (flags & VUNSET)
2297 goto ok;
2298 if ((flags & VSTRFIXED) == 0) {
2299 INT_OFF;
2300 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002301 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002302 *vpp = vp->next;
2303 free(vp);
2304 INT_ON;
2305 } else {
2306 setvar(s, 0, 0);
2307 vp->flags &= ~VEXPORT;
2308 }
2309 ok:
2310 retval = 0;
2311 }
2312 out:
2313 return retval;
2314}
2315
2316/*
2317 * Process a linked list of variable assignments.
2318 */
2319static void
2320listsetvar(struct strlist *list_set_var, int flags)
2321{
2322 struct strlist *lp = list_set_var;
2323
2324 if (!lp)
2325 return;
2326 INT_OFF;
2327 do {
2328 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002329 lp = lp->next;
2330 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002331 INT_ON;
2332}
2333
2334/*
2335 * Generate a list of variables satisfying the given conditions.
2336 */
2337static char **
2338listvars(int on, int off, char ***end)
2339{
2340 struct var **vpp;
2341 struct var *vp;
2342 char **ep;
2343 int mask;
2344
2345 STARTSTACKSTR(ep);
2346 vpp = vartab;
2347 mask = on | off;
2348 do {
2349 for (vp = *vpp; vp; vp = vp->next) {
2350 if ((vp->flags & mask) == on) {
2351 if (ep == stackstrend())
2352 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002353 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002354 }
2355 }
2356 } while (++vpp < vartab + VTABSIZE);
2357 if (ep == stackstrend())
2358 ep = growstackstr();
2359 if (end)
2360 *end = ep;
2361 *ep++ = NULL;
2362 return grabstackstr(ep);
2363}
2364
2365
2366/* ============ Path search helper
2367 *
2368 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002369 * of the path before the first call; path_advance will update
2370 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002371 * the possible path expansions in sequence. If an option (indicated by
2372 * a percent sign) appears in the path entry then the global variable
2373 * pathopt will be set to point to it; otherwise pathopt will be set to
2374 * NULL.
2375 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002376static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002377
2378static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002379path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002380{
2381 const char *p;
2382 char *q;
2383 const char *start;
2384 size_t len;
2385
2386 if (*path == NULL)
2387 return NULL;
2388 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002389 for (p = start; *p && *p != ':' && *p != '%'; p++)
2390 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002391 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2392 while (stackblocksize() < len)
2393 growstackblock();
2394 q = stackblock();
2395 if (p != start) {
2396 memcpy(q, start, p - start);
2397 q += p - start;
2398 *q++ = '/';
2399 }
2400 strcpy(q, name);
2401 pathopt = NULL;
2402 if (*p == '%') {
2403 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002404 while (*p && *p != ':')
2405 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002406 }
2407 if (*p == ':')
2408 *path = p + 1;
2409 else
2410 *path = NULL;
2411 return stalloc(len);
2412}
2413
2414
2415/* ============ Prompt */
2416
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002417static smallint doprompt; /* if set, prompt the user */
2418static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002419
2420#if ENABLE_FEATURE_EDITING
2421static line_input_t *line_input_state;
2422static const char *cmdedit_prompt;
2423static void
2424putprompt(const char *s)
2425{
2426 if (ENABLE_ASH_EXPAND_PRMT) {
2427 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002428 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002429 return;
2430 }
2431 cmdedit_prompt = s;
2432}
2433#else
2434static void
2435putprompt(const char *s)
2436{
2437 out2str(s);
2438}
2439#endif
2440
2441#if ENABLE_ASH_EXPAND_PRMT
2442/* expandstr() needs parsing machinery, so it is far away ahead... */
2443static const char *expandstr(const char *ps);
2444#else
2445#define expandstr(s) s
2446#endif
2447
2448static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002449setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002450{
2451 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002452 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2453
2454 if (!do_set)
2455 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002456
2457 needprompt = 0;
2458
2459 switch (whichprompt) {
2460 case 1:
2461 prompt = ps1val();
2462 break;
2463 case 2:
2464 prompt = ps2val();
2465 break;
2466 default: /* 0 */
2467 prompt = nullstr;
2468 }
2469#if ENABLE_ASH_EXPAND_PRMT
2470 setstackmark(&smark);
2471 stalloc(stackblocksize());
2472#endif
2473 putprompt(expandstr(prompt));
2474#if ENABLE_ASH_EXPAND_PRMT
2475 popstackmark(&smark);
2476#endif
2477}
2478
2479
2480/* ============ The cd and pwd commands */
2481
2482#define CD_PHYSICAL 1
2483#define CD_PRINT 2
2484
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002485static int
2486cdopt(void)
2487{
2488 int flags = 0;
2489 int i, j;
2490
2491 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002492 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002493 if (i != j) {
2494 flags ^= CD_PHYSICAL;
2495 j = i;
2496 }
2497 }
2498
2499 return flags;
2500}
2501
2502/*
2503 * Update curdir (the name of the current directory) in response to a
2504 * cd command.
2505 */
2506static const char *
2507updatepwd(const char *dir)
2508{
2509 char *new;
2510 char *p;
2511 char *cdcomppath;
2512 const char *lim;
2513
2514 cdcomppath = ststrdup(dir);
2515 STARTSTACKSTR(new);
2516 if (*dir != '/') {
2517 if (curdir == nullstr)
2518 return 0;
2519 new = stack_putstr(curdir, new);
2520 }
2521 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002522 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002523 if (*dir != '/') {
2524 if (new[-1] != '/')
2525 USTPUTC('/', new);
2526 if (new > lim && *lim == '/')
2527 lim++;
2528 } else {
2529 USTPUTC('/', new);
2530 cdcomppath++;
2531 if (dir[1] == '/' && dir[2] != '/') {
2532 USTPUTC('/', new);
2533 cdcomppath++;
2534 lim++;
2535 }
2536 }
2537 p = strtok(cdcomppath, "/");
2538 while (p) {
2539 switch (*p) {
2540 case '.':
2541 if (p[1] == '.' && p[2] == '\0') {
2542 while (new > lim) {
2543 STUNPUTC(new);
2544 if (new[-1] == '/')
2545 break;
2546 }
2547 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002548 }
2549 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002550 break;
2551 /* fall through */
2552 default:
2553 new = stack_putstr(p, new);
2554 USTPUTC('/', new);
2555 }
2556 p = strtok(0, "/");
2557 }
2558 if (new > lim)
2559 STUNPUTC(new);
2560 *new = 0;
2561 return stackblock();
2562}
2563
2564/*
2565 * Find out what the current directory is. If we already know the current
2566 * directory, this routine returns immediately.
2567 */
2568static char *
2569getpwd(void)
2570{
Denis Vlasenko01631112007-12-16 17:20:38 +00002571 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002572 return dir ? dir : nullstr;
2573}
2574
2575static void
2576setpwd(const char *val, int setold)
2577{
2578 char *oldcur, *dir;
2579
2580 oldcur = dir = curdir;
2581
2582 if (setold) {
2583 setvar("OLDPWD", oldcur, VEXPORT);
2584 }
2585 INT_OFF;
2586 if (physdir != nullstr) {
2587 if (physdir != oldcur)
2588 free(physdir);
2589 physdir = nullstr;
2590 }
2591 if (oldcur == val || !val) {
2592 char *s = getpwd();
2593 physdir = s;
2594 if (!val)
2595 dir = s;
2596 } else
2597 dir = ckstrdup(val);
2598 if (oldcur != dir && oldcur != nullstr) {
2599 free(oldcur);
2600 }
2601 curdir = dir;
2602 INT_ON;
2603 setvar("PWD", dir, VEXPORT);
2604}
2605
2606static void hashcd(void);
2607
2608/*
2609 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2610 * know that the current directory has changed.
2611 */
2612static int
2613docd(const char *dest, int flags)
2614{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002615 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002616 int err;
2617
2618 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2619
2620 INT_OFF;
2621 if (!(flags & CD_PHYSICAL)) {
2622 dir = updatepwd(dest);
2623 if (dir)
2624 dest = dir;
2625 }
2626 err = chdir(dest);
2627 if (err)
2628 goto out;
2629 setpwd(dir, 1);
2630 hashcd();
2631 out:
2632 INT_ON;
2633 return err;
2634}
2635
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002636static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002637cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002638{
2639 const char *dest;
2640 const char *path;
2641 const char *p;
2642 char c;
2643 struct stat statb;
2644 int flags;
2645
2646 flags = cdopt();
2647 dest = *argptr;
2648 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002649 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002650 else if (LONE_DASH(dest)) {
2651 dest = bltinlookup("OLDPWD");
2652 flags |= CD_PRINT;
2653 }
2654 if (!dest)
2655 dest = nullstr;
2656 if (*dest == '/')
2657 goto step7;
2658 if (*dest == '.') {
2659 c = dest[1];
2660 dotdot:
2661 switch (c) {
2662 case '\0':
2663 case '/':
2664 goto step6;
2665 case '.':
2666 c = dest[2];
2667 if (c != '.')
2668 goto dotdot;
2669 }
2670 }
2671 if (!*dest)
2672 dest = ".";
2673 path = bltinlookup("CDPATH");
2674 if (!path) {
2675 step6:
2676 step7:
2677 p = dest;
2678 goto docd;
2679 }
2680 do {
2681 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002682 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002683 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2684 if (c && c != ':')
2685 flags |= CD_PRINT;
2686 docd:
2687 if (!docd(p, flags))
2688 goto out;
2689 break;
2690 }
2691 } while (path);
2692 ash_msg_and_raise_error("can't cd to %s", dest);
2693 /* NOTREACHED */
2694 out:
2695 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002696 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002697 return 0;
2698}
2699
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002700static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002701pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002702{
2703 int flags;
2704 const char *dir = curdir;
2705
2706 flags = cdopt();
2707 if (flags) {
2708 if (physdir == nullstr)
2709 setpwd(dir, 0);
2710 dir = physdir;
2711 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002712 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002713 return 0;
2714}
2715
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002716
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002717/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002718
Denis Vlasenko834dee72008-10-07 09:18:30 +00002719
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002720#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002721
Eric Andersenc470f442003-07-28 09:56:35 +00002722/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002723#define CWORD 0 /* character is nothing special */
2724#define CNL 1 /* newline character */
2725#define CBACK 2 /* a backslash character */
2726#define CSQUOTE 3 /* single quote */
2727#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002728#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002729#define CBQUOTE 6 /* backwards single quote */
2730#define CVAR 7 /* a dollar sign */
2731#define CENDVAR 8 /* a '}' character */
2732#define CLP 9 /* a left paren in arithmetic */
2733#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002734#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002735#define CCTL 12 /* like CWORD, except it must be escaped */
2736#define CSPCL 13 /* these terminate a word */
2737#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002738
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002739#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002740#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002741# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002742#endif
2743
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002744#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002745
Mike Frysinger98c52642009-04-02 10:02:37 +00002746#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002747# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002748#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002749# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002750#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002751static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002752#if ENABLE_ASH_ALIAS
2753 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2754#endif
2755 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2756 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2757 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2758 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2759 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2760 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2761 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2762 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2763 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2764 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2765 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002766#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002767 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2768 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2769 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2770#endif
2771#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002772};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002773/* Constants below must match table above */
2774enum {
2775#if ENABLE_ASH_ALIAS
2776 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2777#endif
2778 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2779 CNL_CNL_CNL_CNL , /* 2 */
2780 CWORD_CCTL_CCTL_CWORD , /* 3 */
2781 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2782 CVAR_CVAR_CWORD_CVAR , /* 5 */
2783 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2784 CSPCL_CWORD_CWORD_CLP , /* 7 */
2785 CSPCL_CWORD_CWORD_CRP , /* 8 */
2786 CBACK_CBACK_CCTL_CBACK , /* 9 */
2787 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2788 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2789 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2790 CWORD_CWORD_CWORD_CWORD , /* 13 */
2791 CCTL_CCTL_CCTL_CCTL , /* 14 */
2792};
Eric Andersen2870d962001-07-02 17:27:21 +00002793
Denys Vlasenkocd716832009-11-28 22:14:02 +01002794/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2795 * caller must ensure proper cast on it if c is *char_ptr!
2796 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002797/* Values for syntax param */
2798#define BASESYNTAX 0 /* not in quotes */
2799#define DQSYNTAX 1 /* in double quotes */
2800#define SQSYNTAX 2 /* in single quotes */
2801#define ARISYNTAX 3 /* in arithmetic */
2802#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002803
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002804#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002805
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002806static int
2807SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002808{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002809 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002810# if ENABLE_ASH_ALIAS
2811 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002812 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2813 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2814 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2815 11, 3 /* "}~" */
2816 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002817# else
2818 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002819 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2820 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2821 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2822 10, 2 /* "}~" */
2823 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002824# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002825 const char *s;
2826 int indx;
2827
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002828 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002829 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002830# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002831 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002832 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002833 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002834# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002835 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002836 /* Cast is purely for paranoia here,
2837 * just in case someone passed signed char to us */
2838 if ((unsigned char)c >= CTL_FIRST
2839 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002840 ) {
2841 return CCTL;
2842 }
2843 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002844 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002845 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002846 indx = syntax_index_table[s - spec_symbls];
2847 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002848 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002849}
2850
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002851#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002852
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002853static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002854 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002855 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2856 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2857 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2865 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2866 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2879 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2880 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2881 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2882 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2883 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2884 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2885 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2886 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2887 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2888 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2889 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2890 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2891 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2892 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2893 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2894 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2895 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2896 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2897 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2898 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2900 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2901 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2902 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2903 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2905 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2906 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2907 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2908 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2909 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2910 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2914 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2915 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2916 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2917 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2918 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2919 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2938 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2939 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2940 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2943 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2947 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2948 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2949 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2952 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2971 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2972 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2973 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2974 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2975 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2976 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2977 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2978 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2979 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2980 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2981 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2982 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2983 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2985 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2986 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2987 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2988 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2989 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2990 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2991 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2992 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3107 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3108 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3109 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3110 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003111 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003112# if ENABLE_ASH_ALIAS
3113 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3114# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003115};
3116
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003117# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003118
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003119#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003120
Eric Andersen2870d962001-07-02 17:27:21 +00003121
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003122/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003123
Denis Vlasenko131ae172007-02-18 13:00:19 +00003124#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003125
3126#define ALIASINUSE 1
3127#define ALIASDEAD 2
3128
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003129struct alias {
3130 struct alias *next;
3131 char *name;
3132 char *val;
3133 int flag;
3134};
3135
Denis Vlasenko01631112007-12-16 17:20:38 +00003136
3137static struct alias **atab; // [ATABSIZE];
3138#define INIT_G_alias() do { \
3139 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3140} while (0)
3141
Eric Andersen2870d962001-07-02 17:27:21 +00003142
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003143static struct alias **
3144__lookupalias(const char *name) {
3145 unsigned int hashval;
3146 struct alias **app;
3147 const char *p;
3148 unsigned int ch;
3149
3150 p = name;
3151
3152 ch = (unsigned char)*p;
3153 hashval = ch << 4;
3154 while (ch) {
3155 hashval += ch;
3156 ch = (unsigned char)*++p;
3157 }
3158 app = &atab[hashval % ATABSIZE];
3159
3160 for (; *app; app = &(*app)->next) {
3161 if (strcmp(name, (*app)->name) == 0) {
3162 break;
3163 }
3164 }
3165
3166 return app;
3167}
3168
3169static struct alias *
3170lookupalias(const char *name, int check)
3171{
3172 struct alias *ap = *__lookupalias(name);
3173
3174 if (check && ap && (ap->flag & ALIASINUSE))
3175 return NULL;
3176 return ap;
3177}
3178
3179static struct alias *
3180freealias(struct alias *ap)
3181{
3182 struct alias *next;
3183
3184 if (ap->flag & ALIASINUSE) {
3185 ap->flag |= ALIASDEAD;
3186 return ap;
3187 }
3188
3189 next = ap->next;
3190 free(ap->name);
3191 free(ap->val);
3192 free(ap);
3193 return next;
3194}
Eric Andersencb57d552001-06-28 07:25:16 +00003195
Eric Andersenc470f442003-07-28 09:56:35 +00003196static void
3197setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003198{
3199 struct alias *ap, **app;
3200
3201 app = __lookupalias(name);
3202 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003203 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003204 if (ap) {
3205 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003206 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003207 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003208 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003209 ap->flag &= ~ALIASDEAD;
3210 } else {
3211 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003212 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003213 ap->name = ckstrdup(name);
3214 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003215 /*ap->flag = 0; - ckzalloc did it */
3216 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003217 *app = ap;
3218 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003219 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003220}
3221
Eric Andersenc470f442003-07-28 09:56:35 +00003222static int
3223unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003224{
Eric Andersencb57d552001-06-28 07:25:16 +00003225 struct alias **app;
3226
3227 app = __lookupalias(name);
3228
3229 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003230 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003231 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003232 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003233 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003234 }
3235
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003236 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003237}
3238
Eric Andersenc470f442003-07-28 09:56:35 +00003239static void
3240rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003241{
Eric Andersencb57d552001-06-28 07:25:16 +00003242 struct alias *ap, **app;
3243 int i;
3244
Denis Vlasenkob012b102007-02-19 22:43:01 +00003245 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003246 for (i = 0; i < ATABSIZE; i++) {
3247 app = &atab[i];
3248 for (ap = *app; ap; ap = *app) {
3249 *app = freealias(*app);
3250 if (ap == *app) {
3251 app = &ap->next;
3252 }
3253 }
3254 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003255 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003256}
3257
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003258static void
3259printalias(const struct alias *ap)
3260{
3261 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3262}
3263
Eric Andersencb57d552001-06-28 07:25:16 +00003264/*
3265 * TODO - sort output
3266 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003267static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003268aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003269{
3270 char *n, *v;
3271 int ret = 0;
3272 struct alias *ap;
3273
Denis Vlasenko68404f12008-03-17 09:00:54 +00003274 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003275 int i;
3276
Denis Vlasenko68404f12008-03-17 09:00:54 +00003277 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003278 for (ap = atab[i]; ap; ap = ap->next) {
3279 printalias(ap);
3280 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003281 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003282 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003283 }
3284 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003285 v = strchr(n+1, '=');
3286 if (v == NULL) { /* n+1: funny ksh stuff */
3287 ap = *__lookupalias(n);
3288 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003289 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003290 ret = 1;
3291 } else
3292 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003293 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003294 *v++ = '\0';
3295 setalias(n, v);
3296 }
3297 }
3298
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003299 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003300}
3301
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003302static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003303unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003304{
3305 int i;
3306
3307 while ((i = nextopt("a")) != '\0') {
3308 if (i == 'a') {
3309 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003310 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003311 }
3312 }
3313 for (i = 0; *argptr; argptr++) {
3314 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003315 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003316 i = 1;
3317 }
3318 }
3319
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003320 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003321}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003322
Denis Vlasenko131ae172007-02-18 13:00:19 +00003323#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003324
Eric Andersenc470f442003-07-28 09:56:35 +00003325
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003326/* ============ jobs.c */
3327
3328/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003329#define FORK_FG 0
3330#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003331#define FORK_NOJOB 2
3332
3333/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003334#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3335#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3336#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003337
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003338/*
3339 * A job structure contains information about a job. A job is either a
3340 * single process or a set of processes contained in a pipeline. In the
3341 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3342 * array of pids.
3343 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003344struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003345 pid_t ps_pid; /* process id */
3346 int ps_status; /* last process status from wait() */
3347 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003348};
3349
3350struct job {
3351 struct procstat ps0; /* status of process */
3352 struct procstat *ps; /* status or processes when more than one */
3353#if JOBS
3354 int stopstatus; /* status of a stopped job */
3355#endif
3356 uint32_t
3357 nprocs: 16, /* number of processes */
3358 state: 8,
3359#define JOBRUNNING 0 /* at least one proc running */
3360#define JOBSTOPPED 1 /* all procs are stopped */
3361#define JOBDONE 2 /* all procs are completed */
3362#if JOBS
3363 sigint: 1, /* job was killed by SIGINT */
3364 jobctl: 1, /* job running under job control */
3365#endif
3366 waited: 1, /* true if this entry has been waited for */
3367 used: 1, /* true if this entry is in used */
3368 changed: 1; /* true if status has changed */
3369 struct job *prev_job; /* previous job */
3370};
3371
Denis Vlasenko68404f12008-03-17 09:00:54 +00003372static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373static int forkshell(struct job *, union node *, int);
3374static int waitforjob(struct job *);
3375
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003376#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003377enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003378#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003379#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003380static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003381static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003382#endif
3383
3384/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003385 * Ignore a signal.
3386 */
3387static void
3388ignoresig(int signo)
3389{
3390 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3391 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3392 /* No, need to do it */
3393 signal(signo, SIG_IGN);
3394 }
3395 sigmode[signo - 1] = S_HARD_IGN;
3396}
3397
3398/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003399 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003400 */
3401static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003402signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003403{
3404 gotsig[signo - 1] = 1;
3405
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003406 if (signo == SIGINT && !trap[SIGINT]) {
3407 if (!suppress_int) {
3408 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003409 raise_interrupt(); /* does not return */
3410 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003411 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003412 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003413 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003414 }
3415}
3416
3417/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003418 * Set the signal handler for the specified signal. The routine figures
3419 * out what it should be set to.
3420 */
3421static void
3422setsignal(int signo)
3423{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003424 char *t;
3425 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003426 struct sigaction act;
3427
3428 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003429 new_act = S_DFL;
3430 if (t != NULL) { /* trap for this sig is set */
3431 new_act = S_CATCH;
3432 if (t[0] == '\0') /* trap is "": ignore this sig */
3433 new_act = S_IGN;
3434 }
3435
3436 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003437 switch (signo) {
3438 case SIGINT:
3439 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003440 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003441 break;
3442 case SIGQUIT:
3443#if DEBUG
3444 if (debug)
3445 break;
3446#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003447 /* man bash:
3448 * "In all cases, bash ignores SIGQUIT. Non-builtin
3449 * commands run by bash have signal handlers
3450 * set to the values inherited by the shell
3451 * from its parent". */
3452 new_act = S_IGN;
3453 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003454 case SIGTERM:
3455 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003456 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003457 break;
3458#if JOBS
3459 case SIGTSTP:
3460 case SIGTTOU:
3461 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003462 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003463 break;
3464#endif
3465 }
3466 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003467//TODO: if !rootshell, we reset SIGQUIT to DFL,
3468//whereas we have to restore it to what shell got on entry
3469//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003470
3471 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003472 cur_act = *t;
3473 if (cur_act == 0) {
3474 /* current setting is not yet known */
3475 if (sigaction(signo, NULL, &act)) {
3476 /* pretend it worked; maybe we should give a warning,
3477 * but other shells don't. We don't alter sigmode,
3478 * so we retry every time.
3479 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003480 return;
3481 }
3482 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003483 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003484 if (mflag
3485 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3486 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003487 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003488 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003489 }
3490 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003491 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003492 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003493
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003494 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003495 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003496 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003497 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003498 break;
3499 case S_IGN:
3500 act.sa_handler = SIG_IGN;
3501 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003502 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003503
3504 /* flags and mask matter only if !DFL and !IGN, but we do it
3505 * for all cases for more deterministic behavior:
3506 */
3507 act.sa_flags = 0;
3508 sigfillset(&act.sa_mask);
3509
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003510 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003511
3512 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003513}
3514
3515/* mode flags for set_curjob */
3516#define CUR_DELETE 2
3517#define CUR_RUNNING 1
3518#define CUR_STOPPED 0
3519
3520/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003521#define DOWAIT_NONBLOCK WNOHANG
3522#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003523
3524#if JOBS
3525/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003526static int initialpgrp; //references:2
3527static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003528#endif
3529/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003530static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003531/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003532static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003533/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003534static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003535/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003536static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003537
3538static void
3539set_curjob(struct job *jp, unsigned mode)
3540{
3541 struct job *jp1;
3542 struct job **jpp, **curp;
3543
3544 /* first remove from list */
3545 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003546 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003547 jp1 = *jpp;
3548 if (jp1 == jp)
3549 break;
3550 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003551 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003552 *jpp = jp1->prev_job;
3553
3554 /* Then re-insert in correct position */
3555 jpp = curp;
3556 switch (mode) {
3557 default:
3558#if DEBUG
3559 abort();
3560#endif
3561 case CUR_DELETE:
3562 /* job being deleted */
3563 break;
3564 case CUR_RUNNING:
3565 /* newly created job or backgrounded job,
3566 put after all stopped jobs. */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003567 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003568 jp1 = *jpp;
3569#if JOBS
3570 if (!jp1 || jp1->state != JOBSTOPPED)
3571#endif
3572 break;
3573 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003574 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003575 /* FALLTHROUGH */
3576#if JOBS
3577 case CUR_STOPPED:
3578#endif
3579 /* newly stopped job - becomes curjob */
3580 jp->prev_job = *jpp;
3581 *jpp = jp;
3582 break;
3583 }
3584}
3585
3586#if JOBS || DEBUG
3587static int
3588jobno(const struct job *jp)
3589{
3590 return jp - jobtab + 1;
3591}
3592#endif
3593
3594/*
3595 * Convert a job name to a job structure.
3596 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003597#if !JOBS
3598#define getjob(name, getctl) getjob(name)
3599#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003600static struct job *
3601getjob(const char *name, int getctl)
3602{
3603 struct job *jp;
3604 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003605 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003606 unsigned num;
3607 int c;
3608 const char *p;
3609 char *(*match)(const char *, const char *);
3610
3611 jp = curjob;
3612 p = name;
3613 if (!p)
3614 goto currentjob;
3615
3616 if (*p != '%')
3617 goto err;
3618
3619 c = *++p;
3620 if (!c)
3621 goto currentjob;
3622
3623 if (!p[1]) {
3624 if (c == '+' || c == '%') {
3625 currentjob:
3626 err_msg = "No current job";
3627 goto check;
3628 }
3629 if (c == '-') {
3630 if (jp)
3631 jp = jp->prev_job;
3632 err_msg = "No previous job";
3633 check:
3634 if (!jp)
3635 goto err;
3636 goto gotit;
3637 }
3638 }
3639
3640 if (is_number(p)) {
3641 num = atoi(p);
3642 if (num < njobs) {
3643 jp = jobtab + num - 1;
3644 if (jp->used)
3645 goto gotit;
3646 goto err;
3647 }
3648 }
3649
3650 match = prefix;
3651 if (*p == '?') {
3652 match = strstr;
3653 p++;
3654 }
3655
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003656 found = NULL;
3657 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003658 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003659 if (found)
3660 goto err;
3661 found = jp;
3662 err_msg = "%s: ambiguous";
3663 }
3664 jp = jp->prev_job;
3665 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003666 if (!found)
3667 goto err;
3668 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003669
3670 gotit:
3671#if JOBS
3672 err_msg = "job %s not created under job control";
3673 if (getctl && jp->jobctl == 0)
3674 goto err;
3675#endif
3676 return jp;
3677 err:
3678 ash_msg_and_raise_error(err_msg, name);
3679}
3680
3681/*
3682 * Mark a job structure as unused.
3683 */
3684static void
3685freejob(struct job *jp)
3686{
3687 struct procstat *ps;
3688 int i;
3689
3690 INT_OFF;
3691 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003692 if (ps->ps_cmd != nullstr)
3693 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003694 }
3695 if (jp->ps != &jp->ps0)
3696 free(jp->ps);
3697 jp->used = 0;
3698 set_curjob(jp, CUR_DELETE);
3699 INT_ON;
3700}
3701
3702#if JOBS
3703static void
3704xtcsetpgrp(int fd, pid_t pgrp)
3705{
3706 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003707 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003708}
3709
3710/*
3711 * Turn job control on and off.
3712 *
3713 * Note: This code assumes that the third arg to ioctl is a character
3714 * pointer, which is true on Berkeley systems but not System V. Since
3715 * System V doesn't have job control yet, this isn't a problem now.
3716 *
3717 * Called with interrupts off.
3718 */
3719static void
3720setjobctl(int on)
3721{
3722 int fd;
3723 int pgrp;
3724
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003725 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003726 return;
3727 if (on) {
3728 int ofd;
3729 ofd = fd = open(_PATH_TTY, O_RDWR);
3730 if (fd < 0) {
3731 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3732 * That sometimes helps to acquire controlling tty.
3733 * Obviously, a workaround for bugs when someone
3734 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003735 fd = 2;
3736 while (!isatty(fd))
3737 if (--fd < 0)
3738 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 }
3740 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003741 if (ofd >= 0)
3742 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003743 if (fd < 0)
3744 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003745 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003746 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003747 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003748 pgrp = tcgetpgrp(fd);
3749 if (pgrp < 0) {
3750 out:
3751 ash_msg("can't access tty; job control turned off");
3752 mflag = on = 0;
3753 goto close;
3754 }
3755 if (pgrp == getpgrp())
3756 break;
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02003757 killpg_busybox(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01003758 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759 initialpgrp = pgrp;
3760
3761 setsignal(SIGTSTP);
3762 setsignal(SIGTTOU);
3763 setsignal(SIGTTIN);
3764 pgrp = rootpid;
3765 setpgid(0, pgrp);
3766 xtcsetpgrp(fd, pgrp);
3767 } else {
3768 /* turning job control off */
3769 fd = ttyfd;
3770 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003771 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003772 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003773 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774 setpgid(0, pgrp);
3775 setsignal(SIGTSTP);
3776 setsignal(SIGTTOU);
3777 setsignal(SIGTTIN);
3778 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003779 if (fd >= 0)
3780 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003781 fd = -1;
3782 }
3783 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003784 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003785}
3786
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003787static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788killcmd(int argc, char **argv)
3789{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003790 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003791 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003792 do {
3793 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01003794 /*
3795 * "kill %N" - job kill
3796 * Converting to pgrp / pid kill
3797 */
3798 struct job *jp;
3799 char *dst;
3800 int j, n;
3801
3802 jp = getjob(argv[i], 0);
3803 /*
3804 * In jobs started under job control, we signal
3805 * entire process group by kill -PGRP_ID.
3806 * This happens, f.e., in interactive shell.
3807 *
3808 * Otherwise, we signal each child via
3809 * kill PID1 PID2 PID3.
3810 * Testcases:
3811 * sh -c 'sleep 1|sleep 1 & kill %1'
3812 * sh -c 'true|sleep 2 & sleep 1; kill %1'
3813 * sh -c 'true|sleep 1 & sleep 2; kill %1'
3814 */
3815 n = jp->nprocs; /* can't be 0 (I hope) */
3816 if (jp->jobctl)
3817 n = 1;
3818 dst = alloca(n * sizeof(int)*4);
3819 argv[i] = dst;
3820 for (j = 0; j < n; j++) {
3821 struct procstat *ps = &jp->ps[j];
3822 /* Skip non-running and not-stopped members
3823 * (i.e. dead members) of the job
3824 */
3825 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
3826 continue;
3827 /*
3828 * kill_main has matching code to expect
3829 * leading space. Needed to not confuse
3830 * negative pids with "kill -SIGNAL_NO" syntax
3831 */
3832 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
3833 }
3834 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003835 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003836 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003837 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003838 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003839}
3840
3841static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003842showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003843{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003844 struct procstat *ps;
3845 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003846
Denys Vlasenko285ad152009-12-04 23:02:27 +01003847 psend = jp->ps + jp->nprocs;
3848 for (ps = jp->ps + 1; ps < psend; ps++)
3849 printf(" | %s", ps->ps_cmd);
3850 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003851 flush_stdout_stderr();
3852}
3853
3854
3855static int
3856restartjob(struct job *jp, int mode)
3857{
3858 struct procstat *ps;
3859 int i;
3860 int status;
3861 pid_t pgid;
3862
3863 INT_OFF;
3864 if (jp->state == JOBDONE)
3865 goto out;
3866 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003867 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003868 if (mode == FORK_FG)
3869 xtcsetpgrp(ttyfd, pgid);
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02003870 killpg_busybox(pgid, SIGCONT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003871 ps = jp->ps;
3872 i = jp->nprocs;
3873 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003874 if (WIFSTOPPED(ps->ps_status)) {
3875 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003876 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003877 ps++;
3878 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003879 out:
3880 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3881 INT_ON;
3882 return status;
3883}
3884
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003885static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003886fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003887{
3888 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889 int mode;
3890 int retval;
3891
3892 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3893 nextopt(nullstr);
3894 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003895 do {
3896 jp = getjob(*argv, 1);
3897 if (mode == FORK_BG) {
3898 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003899 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003900 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003901 out1str(jp->ps[0].ps_cmd);
3902 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903 retval = restartjob(jp, mode);
3904 } while (*argv && *++argv);
3905 return retval;
3906}
3907#endif
3908
3909static int
3910sprint_status(char *s, int status, int sigonly)
3911{
3912 int col;
3913 int st;
3914
3915 col = 0;
3916 if (!WIFEXITED(status)) {
3917#if JOBS
3918 if (WIFSTOPPED(status))
3919 st = WSTOPSIG(status);
3920 else
3921#endif
3922 st = WTERMSIG(status);
3923 if (sigonly) {
3924 if (st == SIGINT || st == SIGPIPE)
3925 goto out;
3926#if JOBS
3927 if (WIFSTOPPED(status))
3928 goto out;
3929#endif
3930 }
3931 st &= 0x7f;
Tanguy Pruvot8aeb3712011-06-30 08:59:26 +02003932 col = fmtstr(s, 32, "%s", strsignal(st));
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 if (WCOREDUMP(status)) {
3935 col += fmtstr(s + col, 16, " (core dumped)");
3936 }
3937 } else if (!sigonly) {
3938 st = WEXITSTATUS(status);
3939 if (st)
3940 col = fmtstr(s, 16, "Done(%d)", st);
3941 else
3942 col = fmtstr(s, 16, "Done");
3943 }
3944 out:
3945 return col;
3946}
3947
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003948static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003949dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003950{
3951 int pid;
3952 int status;
3953 struct job *jp;
3954 struct job *thisjob;
3955 int state;
3956
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003957 TRACE(("dowait(0x%x) called\n", wait_flags));
3958
3959 /* Do a wait system call. If job control is compiled in, we accept
3960 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3961 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003962 if (doing_jobctl)
3963 wait_flags |= WUNTRACED;
3964 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003965 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3966 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003967 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003968 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003969
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003970 INT_OFF;
3971 thisjob = NULL;
3972 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003973 struct procstat *ps;
3974 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975 if (jp->state == JOBDONE)
3976 continue;
3977 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003978 ps = jp->ps;
3979 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003980 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003981 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003982 TRACE(("Job %d: changing status of proc %d "
3983 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003984 jobno(jp), pid, ps->ps_status, status));
3985 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003986 thisjob = jp;
3987 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003988 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003989 state = JOBRUNNING;
3990#if JOBS
3991 if (state == JOBRUNNING)
3992 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003993 if (WIFSTOPPED(ps->ps_status)) {
3994 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003995 state = JOBSTOPPED;
3996 }
3997#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003998 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003999 if (thisjob)
4000 goto gotjob;
4001 }
4002#if JOBS
4003 if (!WIFSTOPPED(status))
4004#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004005 jobless--;
4006 goto out;
4007
4008 gotjob:
4009 if (state != JOBRUNNING) {
4010 thisjob->changed = 1;
4011
4012 if (thisjob->state != state) {
4013 TRACE(("Job %d: changing state from %d to %d\n",
4014 jobno(thisjob), thisjob->state, state));
4015 thisjob->state = state;
4016#if JOBS
4017 if (state == JOBSTOPPED) {
4018 set_curjob(thisjob, CUR_STOPPED);
4019 }
4020#endif
4021 }
4022 }
4023
4024 out:
4025 INT_ON;
4026
4027 if (thisjob && thisjob == job) {
4028 char s[48 + 1];
4029 int len;
4030
4031 len = sprint_status(s, status, 1);
4032 if (len) {
4033 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004034 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004035 out2str(s);
4036 }
4037 }
4038 return pid;
4039}
4040
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004041static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004042blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004043{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004044 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004045 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004046 raise_exception(EXSIG);
4047 return pid;
4048}
4049
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004050#if JOBS
4051static void
4052showjob(FILE *out, struct job *jp, int mode)
4053{
4054 struct procstat *ps;
4055 struct procstat *psend;
4056 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004057 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004058 char s[80];
4059
4060 ps = jp->ps;
4061
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004062 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004063 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004064 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004065 return;
4066 }
4067
4068 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004069 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004070
4071 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004072 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004073 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004074 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004075
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004076 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004077 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004078
4079 psend = ps + jp->nprocs;
4080
4081 if (jp->state == JOBRUNNING) {
4082 strcpy(s + col, "Running");
4083 col += sizeof("Running") - 1;
4084 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004085 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004086 if (jp->state == JOBSTOPPED)
4087 status = jp->stopstatus;
4088 col += sprint_status(s + col, status, 0);
4089 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004090 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004092 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4093 * or prints several "PID | <cmdN>" lines,
4094 * depending on SHOW_PIDS bit.
4095 * We do not print status of individual processes
4096 * between PID and <cmdN>. bash does it, but not very well:
4097 * first line shows overall job status, not process status,
4098 * making it impossible to know 1st process status.
4099 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004100 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004101 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004102 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004103 s[0] = '\0';
4104 col = 33;
4105 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004106 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004108 fprintf(out, "%s%*c%s%s",
4109 s,
4110 33 - col >= 0 ? 33 - col : 0, ' ',
4111 ps == jp->ps ? "" : "| ",
4112 ps->ps_cmd
4113 );
4114 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004115 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004116
4117 jp->changed = 0;
4118
4119 if (jp->state == JOBDONE) {
4120 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4121 freejob(jp);
4122 }
4123}
4124
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004125/*
4126 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4127 * statuses have changed since the last call to showjobs.
4128 */
4129static void
4130showjobs(FILE *out, int mode)
4131{
4132 struct job *jp;
4133
Denys Vlasenko883cea42009-07-11 15:31:59 +02004134 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004135
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004136 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004137 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138 continue;
4139
4140 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004141 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004142 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004143 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004144 }
4145}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004146
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004147static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004148jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004149{
4150 int mode, m;
4151
4152 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004153 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004154 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004155 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004156 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004157 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004158 }
4159
4160 argv = argptr;
4161 if (*argv) {
4162 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004163 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004164 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004165 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004166 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004167 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004168
4169 return 0;
4170}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004171#endif /* JOBS */
4172
Michael Abbott359da5e2009-12-04 23:03:29 +01004173/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004174static int
4175getstatus(struct job *job)
4176{
4177 int status;
4178 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004179 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004180
Michael Abbott359da5e2009-12-04 23:03:29 +01004181 /* Fetch last member's status */
4182 ps = job->ps + job->nprocs - 1;
4183 status = ps->ps_status;
4184 if (pipefail) {
4185 /* "set -o pipefail" mode: use last _nonzero_ status */
4186 while (status == 0 && --ps >= job->ps)
4187 status = ps->ps_status;
4188 }
4189
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004190 retval = WEXITSTATUS(status);
4191 if (!WIFEXITED(status)) {
4192#if JOBS
4193 retval = WSTOPSIG(status);
4194 if (!WIFSTOPPED(status))
4195#endif
4196 {
4197 /* XXX: limits number of signals */
4198 retval = WTERMSIG(status);
4199#if JOBS
4200 if (retval == SIGINT)
4201 job->sigint = 1;
4202#endif
4203 }
4204 retval += 128;
4205 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004206 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004207 jobno(job), job->nprocs, status, retval));
4208 return retval;
4209}
4210
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004211static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004212waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004213{
4214 struct job *job;
4215 int retval;
4216 struct job *jp;
4217
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004218 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004219 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004220
4221 nextopt(nullstr);
4222 retval = 0;
4223
4224 argv = argptr;
4225 if (!*argv) {
4226 /* wait for all jobs */
4227 for (;;) {
4228 jp = curjob;
4229 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004230 if (!jp) /* no running procs */
4231 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004232 if (jp->state == JOBRUNNING)
4233 break;
4234 jp->waited = 1;
4235 jp = jp->prev_job;
4236 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004237 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004238 /* man bash:
4239 * "When bash is waiting for an asynchronous command via
4240 * the wait builtin, the reception of a signal for which a trap
4241 * has been set will cause the wait builtin to return immediately
4242 * with an exit status greater than 128, immediately after which
4243 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004244 *
4245 * blocking_wait_with_raise_on_sig raises signal handlers
4246 * if it gets no pid (pid < 0). However,
4247 * if child sends us a signal *and immediately exits*,
4248 * blocking_wait_with_raise_on_sig gets pid > 0
4249 * and does not handle pending_sig. Check this case: */
4250 if (pending_sig)
4251 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004252 }
4253 }
4254
4255 retval = 127;
4256 do {
4257 if (**argv != '%') {
4258 pid_t pid = number(*argv);
4259 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004260 while (1) {
4261 if (!job)
4262 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004263 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 break;
4265 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004266 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004267 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004268 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004269 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004270 /* loop until process terminated or stopped */
4271 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004272 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273 job->waited = 1;
4274 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004275 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004276 } while (*++argv);
4277
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004278 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004279 return retval;
4280}
4281
4282static struct job *
4283growjobtab(void)
4284{
4285 size_t len;
4286 ptrdiff_t offset;
4287 struct job *jp, *jq;
4288
4289 len = njobs * sizeof(*jp);
4290 jq = jobtab;
4291 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4292
4293 offset = (char *)jp - (char *)jq;
4294 if (offset) {
4295 /* Relocate pointers */
4296 size_t l = len;
4297
4298 jq = (struct job *)((char *)jq + l);
4299 while (l) {
4300 l -= sizeof(*jp);
4301 jq--;
4302#define joff(p) ((struct job *)((char *)(p) + l))
4303#define jmove(p) (p) = (void *)((char *)(p) + offset)
4304 if (joff(jp)->ps == &jq->ps0)
4305 jmove(joff(jp)->ps);
4306 if (joff(jp)->prev_job)
4307 jmove(joff(jp)->prev_job);
4308 }
4309 if (curjob)
4310 jmove(curjob);
4311#undef joff
4312#undef jmove
4313 }
4314
4315 njobs += 4;
4316 jobtab = jp;
4317 jp = (struct job *)((char *)jp + len);
4318 jq = jp + 3;
4319 do {
4320 jq->used = 0;
4321 } while (--jq >= jp);
4322 return jp;
4323}
4324
4325/*
4326 * Return a new job structure.
4327 * Called with interrupts off.
4328 */
4329static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004330makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004331{
4332 int i;
4333 struct job *jp;
4334
4335 for (i = njobs, jp = jobtab; ; jp++) {
4336 if (--i < 0) {
4337 jp = growjobtab();
4338 break;
4339 }
4340 if (jp->used == 0)
4341 break;
4342 if (jp->state != JOBDONE || !jp->waited)
4343 continue;
4344#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004345 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004346 continue;
4347#endif
4348 freejob(jp);
4349 break;
4350 }
4351 memset(jp, 0, sizeof(*jp));
4352#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004353 /* jp->jobctl is a bitfield.
4354 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004355 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004356 jp->jobctl = 1;
4357#endif
4358 jp->prev_job = curjob;
4359 curjob = jp;
4360 jp->used = 1;
4361 jp->ps = &jp->ps0;
4362 if (nprocs > 1) {
4363 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4364 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004365 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004366 jobno(jp)));
4367 return jp;
4368}
4369
4370#if JOBS
4371/*
4372 * Return a string identifying a command (to be printed by the
4373 * jobs command).
4374 */
4375static char *cmdnextc;
4376
4377static void
4378cmdputs(const char *s)
4379{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004380 static const char vstype[VSTYPE + 1][3] = {
4381 "", "}", "-", "+", "?", "=",
4382 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004383 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004384 };
4385
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004386 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004387 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004388 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004389 unsigned char c;
4390 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004391 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004392
Denys Vlasenko46a14772009-12-10 21:27:13 +01004393 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004394 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4395 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004396 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004397 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004398 switch (c) {
4399 case CTLESC:
4400 c = *p++;
4401 break;
4402 case CTLVAR:
4403 subtype = *p++;
4404 if ((subtype & VSTYPE) == VSLENGTH)
4405 str = "${#";
4406 else
4407 str = "${";
4408 if (!(subtype & VSQUOTE) == !(quoted & 1))
4409 goto dostr;
4410 quoted ^= 1;
4411 c = '"';
4412 break;
4413 case CTLENDVAR:
4414 str = "\"}" + !(quoted & 1);
4415 quoted >>= 1;
4416 subtype = 0;
4417 goto dostr;
4418 case CTLBACKQ:
4419 str = "$(...)";
4420 goto dostr;
4421 case CTLBACKQ+CTLQUOTE:
4422 str = "\"$(...)\"";
4423 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004424#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425 case CTLARI:
4426 str = "$((";
4427 goto dostr;
4428 case CTLENDARI:
4429 str = "))";
4430 goto dostr;
4431#endif
4432 case CTLQUOTEMARK:
4433 quoted ^= 1;
4434 c = '"';
4435 break;
4436 case '=':
4437 if (subtype == 0)
4438 break;
4439 if ((subtype & VSTYPE) != VSNORMAL)
4440 quoted <<= 1;
4441 str = vstype[subtype & VSTYPE];
4442 if (subtype & VSNUL)
4443 c = ':';
4444 else
4445 goto checkstr;
4446 break;
4447 case '\'':
4448 case '\\':
4449 case '"':
4450 case '$':
4451 /* These can only happen inside quotes */
4452 cc[0] = c;
4453 str = cc;
4454 c = '\\';
4455 break;
4456 default:
4457 break;
4458 }
4459 USTPUTC(c, nextc);
4460 checkstr:
4461 if (!str)
4462 continue;
4463 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004464 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004465 USTPUTC(c, nextc);
4466 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004467 } /* while *p++ not NUL */
4468
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469 if (quoted & 1) {
4470 USTPUTC('"', nextc);
4471 }
4472 *nextc = 0;
4473 cmdnextc = nextc;
4474}
4475
4476/* cmdtxt() and cmdlist() call each other */
4477static void cmdtxt(union node *n);
4478
4479static void
4480cmdlist(union node *np, int sep)
4481{
4482 for (; np; np = np->narg.next) {
4483 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004484 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485 cmdtxt(np);
4486 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004487 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488 }
4489}
4490
4491static void
4492cmdtxt(union node *n)
4493{
4494 union node *np;
4495 struct nodelist *lp;
4496 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004497
4498 if (!n)
4499 return;
4500 switch (n->type) {
4501 default:
4502#if DEBUG
4503 abort();
4504#endif
4505 case NPIPE:
4506 lp = n->npipe.cmdlist;
4507 for (;;) {
4508 cmdtxt(lp->n);
4509 lp = lp->next;
4510 if (!lp)
4511 break;
4512 cmdputs(" | ");
4513 }
4514 break;
4515 case NSEMI:
4516 p = "; ";
4517 goto binop;
4518 case NAND:
4519 p = " && ";
4520 goto binop;
4521 case NOR:
4522 p = " || ";
4523 binop:
4524 cmdtxt(n->nbinary.ch1);
4525 cmdputs(p);
4526 n = n->nbinary.ch2;
4527 goto donode;
4528 case NREDIR:
4529 case NBACKGND:
4530 n = n->nredir.n;
4531 goto donode;
4532 case NNOT:
4533 cmdputs("!");
4534 n = n->nnot.com;
4535 donode:
4536 cmdtxt(n);
4537 break;
4538 case NIF:
4539 cmdputs("if ");
4540 cmdtxt(n->nif.test);
4541 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004542 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004543 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004544 cmdputs("; else ");
4545 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004546 } else {
4547 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004548 }
4549 p = "; fi";
4550 goto dotail;
4551 case NSUBSHELL:
4552 cmdputs("(");
4553 n = n->nredir.n;
4554 p = ")";
4555 goto dotail;
4556 case NWHILE:
4557 p = "while ";
4558 goto until;
4559 case NUNTIL:
4560 p = "until ";
4561 until:
4562 cmdputs(p);
4563 cmdtxt(n->nbinary.ch1);
4564 n = n->nbinary.ch2;
4565 p = "; done";
4566 dodo:
4567 cmdputs("; do ");
4568 dotail:
4569 cmdtxt(n);
4570 goto dotail2;
4571 case NFOR:
4572 cmdputs("for ");
4573 cmdputs(n->nfor.var);
4574 cmdputs(" in ");
4575 cmdlist(n->nfor.args, 1);
4576 n = n->nfor.body;
4577 p = "; done";
4578 goto dodo;
4579 case NDEFUN:
4580 cmdputs(n->narg.text);
4581 p = "() { ... }";
4582 goto dotail2;
4583 case NCMD:
4584 cmdlist(n->ncmd.args, 1);
4585 cmdlist(n->ncmd.redirect, 0);
4586 break;
4587 case NARG:
4588 p = n->narg.text;
4589 dotail2:
4590 cmdputs(p);
4591 break;
4592 case NHERE:
4593 case NXHERE:
4594 p = "<<...";
4595 goto dotail2;
4596 case NCASE:
4597 cmdputs("case ");
4598 cmdputs(n->ncase.expr->narg.text);
4599 cmdputs(" in ");
4600 for (np = n->ncase.cases; np; np = np->nclist.next) {
4601 cmdtxt(np->nclist.pattern);
4602 cmdputs(") ");
4603 cmdtxt(np->nclist.body);
4604 cmdputs(";; ");
4605 }
4606 p = "esac";
4607 goto dotail2;
4608 case NTO:
4609 p = ">";
4610 goto redir;
4611 case NCLOBBER:
4612 p = ">|";
4613 goto redir;
4614 case NAPPEND:
4615 p = ">>";
4616 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004617#if ENABLE_ASH_BASH_COMPAT
4618 case NTO2:
4619#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004620 case NTOFD:
4621 p = ">&";
4622 goto redir;
4623 case NFROM:
4624 p = "<";
4625 goto redir;
4626 case NFROMFD:
4627 p = "<&";
4628 goto redir;
4629 case NFROMTO:
4630 p = "<>";
4631 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004632 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004633 cmdputs(p);
4634 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004635 cmdputs(utoa(n->ndup.dupfd));
4636 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004637 }
4638 n = n->nfile.fname;
4639 goto donode;
4640 }
4641}
4642
4643static char *
4644commandtext(union node *n)
4645{
4646 char *name;
4647
4648 STARTSTACKSTR(cmdnextc);
4649 cmdtxt(n);
4650 name = stackblock();
4651 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4652 name, cmdnextc, cmdnextc));
4653 return ckstrdup(name);
4654}
4655#endif /* JOBS */
4656
4657/*
4658 * Fork off a subshell. If we are doing job control, give the subshell its
4659 * own process group. Jp is a job structure that the job is to be added to.
4660 * N is the command that will be evaluated by the child. Both jp and n may
4661 * be NULL. The mode parameter can be one of the following:
4662 * FORK_FG - Fork off a foreground process.
4663 * FORK_BG - Fork off a background process.
4664 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4665 * process group even if job control is on.
4666 *
4667 * When job control is turned off, background processes have their standard
4668 * input redirected to /dev/null (except for the second and later processes
4669 * in a pipeline).
4670 *
4671 * Called with interrupts off.
4672 */
4673/*
4674 * Clear traps on a fork.
4675 */
4676static void
4677clear_traps(void)
4678{
4679 char **tp;
4680
4681 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004682 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004683 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004684 if (trap_ptr == trap)
4685 free(*tp);
4686 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004687 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004688 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004689 setsignal(tp - trap);
4690 INT_ON;
4691 }
4692 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004693 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004694}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004695
4696/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004697static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004698
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004699/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004700static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004701forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004702{
4703 int oldlvl;
4704
4705 TRACE(("Child shell %d\n", getpid()));
4706 oldlvl = shlvl;
4707 shlvl++;
4708
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004709 /* man bash: "Non-builtin commands run by bash have signal handlers
4710 * set to the values inherited by the shell from its parent".
4711 * Do we do it correctly? */
4712
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004713 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004714
4715 if (mode == FORK_NOJOB /* is it `xxx` ? */
4716 && n && n->type == NCMD /* is it single cmd? */
4717 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004718 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004719 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4720 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4721 ) {
4722 TRACE(("Trap hack\n"));
4723 /* Awful hack for `trap` or $(trap).
4724 *
4725 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4726 * contains an example where "trap" is executed in a subshell:
4727 *
4728 * save_traps=$(trap)
4729 * ...
4730 * eval "$save_traps"
4731 *
4732 * Standard does not say that "trap" in subshell shall print
4733 * parent shell's traps. It only says that its output
4734 * must have suitable form, but then, in the above example
4735 * (which is not supposed to be normative), it implies that.
4736 *
4737 * bash (and probably other shell) does implement it
4738 * (traps are reset to defaults, but "trap" still shows them),
4739 * but as a result, "trap" logic is hopelessly messed up:
4740 *
4741 * # trap
4742 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4743 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4744 * # true | trap <--- trap is in subshell - no output (ditto)
4745 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4746 * trap -- 'echo Ho' SIGWINCH
4747 * # echo `(trap)` <--- in subshell in subshell - output
4748 * trap -- 'echo Ho' SIGWINCH
4749 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4750 * trap -- 'echo Ho' SIGWINCH
4751 *
4752 * The rules when to forget and when to not forget traps
4753 * get really complex and nonsensical.
4754 *
4755 * Our solution: ONLY bare $(trap) or `trap` is special.
4756 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004757 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004758 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004759 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004760 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004761 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004762#if JOBS
4763 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004764 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004765 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004766 pid_t pgrp;
4767
4768 if (jp->nprocs == 0)
4769 pgrp = getpid();
4770 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004771 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004772 /* this can fail because we are doing it in the parent also */
4773 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004774 if (mode == FORK_FG)
4775 xtcsetpgrp(ttyfd, pgrp);
4776 setsignal(SIGTSTP);
4777 setsignal(SIGTTOU);
4778 } else
4779#endif
4780 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004781 /* man bash: "When job control is not in effect,
4782 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004783 ignoresig(SIGINT);
4784 ignoresig(SIGQUIT);
4785 if (jp->nprocs == 0) {
4786 close(0);
4787 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004788 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004789 }
4790 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004791 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004792 if (iflag) { /* why if iflag only? */
4793 setsignal(SIGINT);
4794 setsignal(SIGTERM);
4795 }
4796 /* man bash:
4797 * "In all cases, bash ignores SIGQUIT. Non-builtin
4798 * commands run by bash have signal handlers
4799 * set to the values inherited by the shell
4800 * from its parent".
4801 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004802 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004803 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004804#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004805 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004806 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004807 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004808 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004809 /* "jobs": we do not want to clear job list for it,
4810 * instead we remove only _its_ own_ job from job list.
4811 * This makes "jobs .... | cat" more useful.
4812 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004813 freejob(curjob);
4814 return;
4815 }
4816#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004817 for (jp = curjob; jp; jp = jp->prev_job)
4818 freejob(jp);
4819 jobless = 0;
4820}
4821
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004822/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004823#if !JOBS
4824#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4825#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004826static void
4827forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4828{
4829 TRACE(("In parent shell: child = %d\n", pid));
4830 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004831 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4832 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004833 jobless++;
4834 return;
4835 }
4836#if JOBS
4837 if (mode != FORK_NOJOB && jp->jobctl) {
4838 int pgrp;
4839
4840 if (jp->nprocs == 0)
4841 pgrp = pid;
4842 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004843 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004844 /* This can fail because we are doing it in the child also */
4845 setpgid(pid, pgrp);
4846 }
4847#endif
4848 if (mode == FORK_BG) {
4849 backgndpid = pid; /* set $! */
4850 set_curjob(jp, CUR_RUNNING);
4851 }
4852 if (jp) {
4853 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004854 ps->ps_pid = pid;
4855 ps->ps_status = -1;
4856 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004857#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004858 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004859 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004860#endif
4861 }
4862}
4863
4864static int
4865forkshell(struct job *jp, union node *n, int mode)
4866{
4867 int pid;
4868
4869 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4870 pid = fork();
4871 if (pid < 0) {
4872 TRACE(("Fork failed, errno=%d", errno));
4873 if (jp)
4874 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004875 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004876 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004877 if (pid == 0) {
4878 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004879 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004880 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004881 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004882 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004883 return pid;
4884}
4885
4886/*
4887 * Wait for job to finish.
4888 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004889 * Under job control we have the problem that while a child process
4890 * is running interrupts generated by the user are sent to the child
4891 * but not to the shell. This means that an infinite loop started by
4892 * an interactive user may be hard to kill. With job control turned off,
4893 * an interactive user may place an interactive program inside a loop.
4894 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004895 * these interrupts to also abort the loop. The approach we take here
4896 * is to have the shell ignore interrupt signals while waiting for a
4897 * foreground process to terminate, and then send itself an interrupt
4898 * signal if the child process was terminated by an interrupt signal.
4899 * Unfortunately, some programs want to do a bit of cleanup and then
4900 * exit on interrupt; unless these processes terminate themselves by
4901 * sending a signal to themselves (instead of calling exit) they will
4902 * confuse this approach.
4903 *
4904 * Called with interrupts off.
4905 */
4906static int
4907waitforjob(struct job *jp)
4908{
4909 int st;
4910
4911 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004912
4913 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004914 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004915 /* In non-interactive shells, we _can_ get
4916 * a keyboard signal here and be EINTRed,
4917 * but we just loop back, waiting for command to complete.
4918 *
4919 * man bash:
4920 * "If bash is waiting for a command to complete and receives
4921 * a signal for which a trap has been set, the trap
4922 * will not be executed until the command completes."
4923 *
4924 * Reality is that even if trap is not set, bash
4925 * will not act on the signal until command completes.
4926 * Try this. sleep5intoff.c:
4927 * #include <signal.h>
4928 * #include <unistd.h>
4929 * int main() {
4930 * sigset_t set;
4931 * sigemptyset(&set);
4932 * sigaddset(&set, SIGINT);
4933 * sigaddset(&set, SIGQUIT);
4934 * sigprocmask(SIG_BLOCK, &set, NULL);
4935 * sleep(5);
4936 * return 0;
4937 * }
4938 * $ bash -c './sleep5intoff; echo hi'
4939 * ^C^C^C^C <--- pressing ^C once a second
4940 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004941 * $ bash -c './sleep5intoff; echo hi'
4942 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4943 * $ _
4944 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004945 dowait(DOWAIT_BLOCK, jp);
4946 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004947 INT_ON;
4948
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004949 st = getstatus(jp);
4950#if JOBS
4951 if (jp->jobctl) {
4952 xtcsetpgrp(ttyfd, rootpid);
4953 /*
4954 * This is truly gross.
4955 * If we're doing job control, then we did a TIOCSPGRP which
4956 * caused us (the shell) to no longer be in the controlling
4957 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4958 * intuit from the subprocess exit status whether a SIGINT
4959 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4960 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004961 if (jp->sigint) /* TODO: do the same with all signals */
4962 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004963 }
4964 if (jp->state == JOBDONE)
4965#endif
4966 freejob(jp);
4967 return st;
4968}
4969
4970/*
4971 * return 1 if there are stopped jobs, otherwise 0
4972 */
4973static int
4974stoppedjobs(void)
4975{
4976 struct job *jp;
4977 int retval;
4978
4979 retval = 0;
4980 if (job_warning)
4981 goto out;
4982 jp = curjob;
4983 if (jp && jp->state == JOBSTOPPED) {
4984 out2str("You have stopped jobs.\n");
4985 job_warning = 2;
4986 retval++;
4987 }
4988 out:
4989 return retval;
4990}
4991
4992
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004993/* ============ redir.c
4994 *
4995 * Code for dealing with input/output redirection.
4996 */
4997
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004998#undef EMPTY
4999#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005000#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005001#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005002
5003/*
5004 * Open a file in noclobber mode.
5005 * The code was copied from bash.
5006 */
5007static int
5008noclobberopen(const char *fname)
5009{
5010 int r, fd;
5011 struct stat finfo, finfo2;
5012
5013 /*
5014 * If the file exists and is a regular file, return an error
5015 * immediately.
5016 */
5017 r = stat(fname, &finfo);
5018 if (r == 0 && S_ISREG(finfo.st_mode)) {
5019 errno = EEXIST;
5020 return -1;
5021 }
5022
5023 /*
5024 * If the file was not present (r != 0), make sure we open it
5025 * exclusively so that if it is created before we open it, our open
5026 * will fail. Make sure that we do not truncate an existing file.
5027 * Note that we don't turn on O_EXCL unless the stat failed -- if the
5028 * file was not a regular file, we leave O_EXCL off.
5029 */
5030 if (r != 0)
5031 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5032 fd = open(fname, O_WRONLY|O_CREAT, 0666);
5033
5034 /* If the open failed, return the file descriptor right away. */
5035 if (fd < 0)
5036 return fd;
5037
5038 /*
5039 * OK, the open succeeded, but the file may have been changed from a
5040 * non-regular file to a regular file between the stat and the open.
5041 * We are assuming that the O_EXCL open handles the case where FILENAME
5042 * did not exist and is symlinked to an existing file between the stat
5043 * and open.
5044 */
5045
5046 /*
5047 * If we can open it and fstat the file descriptor, and neither check
5048 * revealed that it was a regular file, and the file has not been
5049 * replaced, return the file descriptor.
5050 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005051 if (fstat(fd, &finfo2) == 0
5052 && !S_ISREG(finfo2.st_mode)
5053 && finfo.st_dev == finfo2.st_dev
5054 && finfo.st_ino == finfo2.st_ino
5055 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005056 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005057 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005058
5059 /* The file has been replaced. badness. */
5060 close(fd);
5061 errno = EEXIST;
5062 return -1;
5063}
5064
5065/*
5066 * Handle here documents. Normally we fork off a process to write the
5067 * data to a pipe. If the document is short, we can stuff the data in
5068 * the pipe without forking.
5069 */
5070/* openhere needs this forward reference */
5071static void expandhere(union node *arg, int fd);
5072static int
5073openhere(union node *redir)
5074{
5075 int pip[2];
5076 size_t len = 0;
5077
5078 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005079 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005080 if (redir->type == NHERE) {
5081 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005082 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005083 full_write(pip[1], redir->nhere.doc->narg.text, len);
5084 goto out;
5085 }
5086 }
5087 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005088 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005089 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005090 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5091 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5092 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5093 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005094 signal(SIGPIPE, SIG_DFL);
5095 if (redir->type == NHERE)
5096 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005097 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005098 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005099 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005100 }
5101 out:
5102 close(pip[1]);
5103 return pip[0];
5104}
5105
5106static int
5107openredirect(union node *redir)
5108{
5109 char *fname;
5110 int f;
5111
5112 switch (redir->nfile.type) {
5113 case NFROM:
5114 fname = redir->nfile.expfname;
5115 f = open(fname, O_RDONLY);
5116 if (f < 0)
5117 goto eopen;
5118 break;
5119 case NFROMTO:
5120 fname = redir->nfile.expfname;
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) {
5131 fname = redir->nfile.expfname;
5132 f = noclobberopen(fname);
5133 if (f < 0)
5134 goto ecreate;
5135 break;
5136 }
5137 /* FALLTHROUGH */
5138 case NCLOBBER:
5139 fname = redir->nfile.expfname;
5140 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5141 if (f < 0)
5142 goto ecreate;
5143 break;
5144 case NAPPEND:
5145 fname = redir->nfile.expfname;
5146 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5147 if (f < 0)
5148 goto ecreate;
5149 break;
5150 default:
5151#if DEBUG
5152 abort();
5153#endif
5154 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005155/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005156// case NTOFD:
5157// case NFROMFD:
5158// f = -1;
5159// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005160 case NHERE:
5161 case NXHERE:
5162 f = openhere(redir);
5163 break;
5164 }
5165
5166 return f;
5167 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005168 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005169 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005170 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005171}
5172
5173/*
5174 * Copy a file descriptor to be >= to. Returns -1
5175 * if the source file descriptor is closed, EMPTY if there are no unused
5176 * file descriptors left.
5177 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005178/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5179 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005180enum {
5181 COPYFD_EXACT = (int)~(INT_MAX),
5182 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5183};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005184static int
5185copyfd(int from, int to)
5186{
5187 int newfd;
5188
Denis Vlasenko5a867312008-07-24 19:46:38 +00005189 if (to & COPYFD_EXACT) {
5190 to &= ~COPYFD_EXACT;
5191 /*if (from != to)*/
5192 newfd = dup2(from, to);
5193 } else {
5194 newfd = fcntl(from, F_DUPFD, to);
5195 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005196 if (newfd < 0) {
5197 if (errno == EMFILE)
5198 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005199 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005200 ash_msg_and_raise_error("%d: %m", from);
5201 }
5202 return newfd;
5203}
5204
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005205/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005206struct two_fd_t {
5207 int orig, copy;
5208};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005209struct redirtab {
5210 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005211 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005212 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005213 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005214};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005215#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005216
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005217static int need_to_remember(struct redirtab *rp, int fd)
5218{
5219 int i;
5220
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005221 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005222 return 0;
5223
5224 for (i = 0; i < rp->pair_count; i++) {
5225 if (rp->two_fd[i].orig == fd) {
5226 /* already remembered */
5227 return 0;
5228 }
5229 }
5230 return 1;
5231}
5232
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005233/* "hidden" fd is a fd used to read scripts, or a copy of such */
5234static int is_hidden_fd(struct redirtab *rp, int fd)
5235{
5236 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005237 struct parsefile *pf;
5238
5239 if (fd == -1)
5240 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005241 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005242 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005243 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005244 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005245 * $ ash # running ash interactively
5246 * $ . ./script.sh
5247 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005248 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005249 * it's still ok to use it: "read" builtin uses it,
5250 * why should we cripple "exec" builtin?
5251 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005252 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005253 return 1;
5254 }
5255 pf = pf->prev;
5256 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005257
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005258 if (!rp)
5259 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005260 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005261 fd |= COPYFD_RESTORE;
5262 for (i = 0; i < rp->pair_count; i++) {
5263 if (rp->two_fd[i].copy == fd) {
5264 return 1;
5265 }
5266 }
5267 return 0;
5268}
5269
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005270/*
5271 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5272 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005273 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005274 */
5275/* flags passed to redirect */
5276#define REDIR_PUSH 01 /* save previous values of file descriptors */
5277#define REDIR_SAVEFD2 03 /* set preverrout */
5278static void
5279redirect(union node *redir, int flags)
5280{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005281 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005282 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005283 int i;
5284 int fd;
5285 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005286 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005287
Denis Vlasenko01631112007-12-16 17:20:38 +00005288 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005289 if (!redir) {
5290 return;
5291 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005292
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005293 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005294 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005295 INT_OFF;
5296 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005297 union node *tmp = redir;
5298 do {
5299 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005300#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005301 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005302 sv_pos++;
5303#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005304 tmp = tmp->nfile.next;
5305 } while (tmp);
5306 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005307 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005308 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005309 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005310 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005311 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005312 while (sv_pos > 0) {
5313 sv_pos--;
5314 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5315 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005316 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005317
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005318 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005319 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005320 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005321 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005322 right_fd = redir->ndup.dupfd;
5323 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005324 /* redirect from/to same file descriptor? */
5325 if (right_fd == fd)
5326 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005327 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005328 if (is_hidden_fd(sv, right_fd)) {
5329 errno = EBADF; /* as if it is closed */
5330 ash_msg_and_raise_error("%d: %m", right_fd);
5331 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005332 newfd = -1;
5333 } else {
5334 newfd = openredirect(redir); /* always >= 0 */
5335 if (fd == newfd) {
5336 /* Descriptor wasn't open before redirect.
5337 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005338 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005339 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005340 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005341 continue;
5342 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005343 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005344#if ENABLE_ASH_BASH_COMPAT
5345 redirect_more:
5346#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005347 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005348 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005349 /* Careful to not accidentally "save"
5350 * to the same fd as right side fd in N>&M */
5351 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5352 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005353/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5354 * are closed in popredir() in the child, preventing them from leaking
5355 * into child. (popredir() also cleans up the mess in case of failures)
5356 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005357 if (i == -1) {
5358 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005359 if (i != EBADF) {
5360 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005361 if (newfd >= 0)
5362 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005363 errno = i;
5364 ash_msg_and_raise_error("%d: %m", fd);
5365 /* NOTREACHED */
5366 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005367 /* EBADF: it is not open - good, remember to close it */
5368 remember_to_close:
5369 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005370 } else { /* fd is open, save its copy */
5371 /* "exec fd>&-" should not close fds
5372 * which point to script file(s).
5373 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005374 if (is_hidden_fd(sv, fd))
5375 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005376 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005377 if (fd == 2)
5378 copied_fd2 = i;
5379 sv->two_fd[sv_pos].orig = fd;
5380 sv->two_fd[sv_pos].copy = i;
5381 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005382 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005383 if (newfd < 0) {
5384 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005385 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005386 /* Don't want to trigger debugging */
5387 if (fd != -1)
5388 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005389 } else {
5390 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005391 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005392 } else if (fd != newfd) { /* move newfd to fd */
5393 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005394#if ENABLE_ASH_BASH_COMPAT
5395 if (!(redir->nfile.type == NTO2 && fd == 2))
5396#endif
5397 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005398 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005399#if ENABLE_ASH_BASH_COMPAT
5400 if (redir->nfile.type == NTO2 && fd == 1) {
5401 /* We already redirected it to fd 1, now copy it to 2 */
5402 newfd = 1;
5403 fd = 2;
5404 goto redirect_more;
5405 }
5406#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005407 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005408
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005409 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005410 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5411 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005412}
5413
5414/*
5415 * Undo the effects of the last redirection.
5416 */
5417static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005418popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005419{
5420 struct redirtab *rp;
5421 int i;
5422
Denis Vlasenko01631112007-12-16 17:20:38 +00005423 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005424 return;
5425 INT_OFF;
5426 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005427 for (i = 0; i < rp->pair_count; i++) {
5428 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005429 int copy = rp->two_fd[i].copy;
5430 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005431 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005432 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005433 continue;
5434 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005435 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005436 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005437 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005438 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005439 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005440 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005441 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005442 }
5443 }
5444 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005445 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005446 free(rp);
5447 INT_ON;
5448}
5449
5450/*
5451 * Undo all redirections. Called on error or interrupt.
5452 */
5453
5454/*
5455 * Discard all saved file descriptors.
5456 */
5457static void
5458clearredir(int drop)
5459{
5460 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005461 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005462 if (!redirlist)
5463 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005464 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005465 }
5466}
5467
5468static int
5469redirectsafe(union node *redir, int flags)
5470{
5471 int err;
5472 volatile int saveint;
5473 struct jmploc *volatile savehandler = exception_handler;
5474 struct jmploc jmploc;
5475
5476 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005477 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5478 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005479 if (!err) {
5480 exception_handler = &jmploc;
5481 redirect(redir, flags);
5482 }
5483 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005484 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005485 longjmp(exception_handler->loc, 1);
5486 RESTORE_INT(saveint);
5487 return err;
5488}
5489
5490
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005491/* ============ Routines to expand arguments to commands
5492 *
5493 * We have to deal with backquotes, shell variables, and file metacharacters.
5494 */
5495
Mike Frysinger98c52642009-04-02 10:02:37 +00005496#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005497static arith_t
5498ash_arith(const char *s)
5499{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005500 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005501 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005502
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005503 math_state.lookupvar = lookupvar;
5504 math_state.setvar = setvar2;
5505 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005506
5507 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005508 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005509 if (math_state.errmsg)
5510 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005511 INT_ON;
5512
5513 return result;
5514}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005515#endif
5516
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005517/*
5518 * expandarg flags
5519 */
5520#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5521#define EXP_TILDE 0x2 /* do normal tilde expansion */
5522#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5523#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5524#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5525#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5526#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5527#define EXP_WORD 0x80 /* expand word in parameter expansion */
5528#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5529/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005530 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005531 */
5532#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5533#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5534#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5535#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5536#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5537
5538/*
5539 * Structure specifying which parts of the string should be searched
5540 * for IFS characters.
5541 */
5542struct ifsregion {
5543 struct ifsregion *next; /* next region in list */
5544 int begoff; /* offset of start of region */
5545 int endoff; /* offset of end of region */
5546 int nulonly; /* search for nul bytes only */
5547};
5548
5549struct arglist {
5550 struct strlist *list;
5551 struct strlist **lastp;
5552};
5553
5554/* output of current string */
5555static char *expdest;
5556/* list of back quote expressions */
5557static struct nodelist *argbackq;
5558/* first struct in list of ifs regions */
5559static struct ifsregion ifsfirst;
5560/* last struct in list */
5561static struct ifsregion *ifslastp;
5562/* holds expanded arg list */
5563static struct arglist exparg;
5564
5565/*
5566 * Our own itoa().
5567 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005568#if !ENABLE_SH_MATH_SUPPORT
5569/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5570typedef long arith_t;
5571# define ARITH_FMT "%ld"
5572#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005573static int
5574cvtnum(arith_t num)
5575{
5576 int len;
5577
5578 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005579 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005580 STADJUST(len, expdest);
5581 return len;
5582}
5583
5584static size_t
5585esclen(const char *start, const char *p)
5586{
5587 size_t esc = 0;
5588
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005589 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005590 esc++;
5591 }
5592 return esc;
5593}
5594
5595/*
5596 * Remove any CTLESC characters from a string.
5597 */
5598static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005599rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005600{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005601 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005602
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005603 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005604 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005605 unsigned protect_against_glob;
5606 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005607
5608 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005609 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005610 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005611
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005612 q = p;
5613 r = str;
5614 if (flag & RMESCAPE_ALLOC) {
5615 size_t len = p - str;
5616 size_t fulllen = len + strlen(p) + 1;
5617
5618 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005619 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005620 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005621 /* p and str may be invalidated by makestrspace */
5622 str = (char *)stackblock() + strloc;
5623 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005624 } else if (flag & RMESCAPE_HEAP) {
5625 r = ckmalloc(fulllen);
5626 } else {
5627 r = stalloc(fulllen);
5628 }
5629 q = r;
5630 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005631 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005632 }
5633 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005634
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005635 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5636 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005637 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005638 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005639 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005640// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5641// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5642// Note: both inquotes and protect_against_glob only affect whether
5643// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005644 inquotes = ~inquotes;
5645 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005646 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005647 continue;
5648 }
5649 if (*p == '\\') {
5650 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005651 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005652 goto copy;
5653 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005654 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005655 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005656 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005657 *q++ = '\\';
5658 }
5659 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005660 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005661 copy:
5662 *q++ = *p++;
5663 }
5664 *q = '\0';
5665 if (flag & RMESCAPE_GROW) {
5666 expdest = r;
5667 STADJUST(q - r + 1, expdest);
5668 }
5669 return r;
5670}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005671#define pmatch(a, b) !fnmatch((a), (b), 0)
5672
5673/*
5674 * Prepare a pattern for a expmeta (internal glob(3)) call.
5675 *
5676 * Returns an stalloced string.
5677 */
5678static char *
5679preglob(const char *pattern, int quoted, int flag)
5680{
5681 flag |= RMESCAPE_GLOB;
5682 if (quoted) {
5683 flag |= RMESCAPE_QUOTED;
5684 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005685 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005686}
5687
5688/*
5689 * Put a string on the stack.
5690 */
5691static void
5692memtodest(const char *p, size_t len, int syntax, int quotes)
5693{
5694 char *q = expdest;
5695
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005696 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005697
5698 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005699 unsigned char c = *p++;
5700 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005701 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005702 if (quotes) {
5703 int n = SIT(c, syntax);
5704 if (n == CCTL || n == CBACK)
5705 USTPUTC(CTLESC, q);
5706 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005707 USTPUTC(c, q);
5708 }
5709
5710 expdest = q;
5711}
5712
5713static void
5714strtodest(const char *p, int syntax, int quotes)
5715{
5716 memtodest(p, strlen(p), syntax, quotes);
5717}
5718
5719/*
5720 * Record the fact that we have to scan this region of the
5721 * string for IFS characters.
5722 */
5723static void
5724recordregion(int start, int end, int nulonly)
5725{
5726 struct ifsregion *ifsp;
5727
5728 if (ifslastp == NULL) {
5729 ifsp = &ifsfirst;
5730 } else {
5731 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005732 ifsp = ckzalloc(sizeof(*ifsp));
5733 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005734 ifslastp->next = ifsp;
5735 INT_ON;
5736 }
5737 ifslastp = ifsp;
5738 ifslastp->begoff = start;
5739 ifslastp->endoff = end;
5740 ifslastp->nulonly = nulonly;
5741}
5742
5743static void
5744removerecordregions(int endoff)
5745{
5746 if (ifslastp == NULL)
5747 return;
5748
5749 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005750 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005751 struct ifsregion *ifsp;
5752 INT_OFF;
5753 ifsp = ifsfirst.next->next;
5754 free(ifsfirst.next);
5755 ifsfirst.next = ifsp;
5756 INT_ON;
5757 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005758 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005759 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005760 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005761 ifslastp = &ifsfirst;
5762 ifsfirst.endoff = endoff;
5763 }
5764 return;
5765 }
5766
5767 ifslastp = &ifsfirst;
5768 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005769 ifslastp = ifslastp->next;
5770 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005771 struct ifsregion *ifsp;
5772 INT_OFF;
5773 ifsp = ifslastp->next->next;
5774 free(ifslastp->next);
5775 ifslastp->next = ifsp;
5776 INT_ON;
5777 }
5778 if (ifslastp->endoff > endoff)
5779 ifslastp->endoff = endoff;
5780}
5781
5782static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005783exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005784{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005785 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005786 char *name;
5787 struct passwd *pw;
5788 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005789 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005790 int startloc;
5791
5792 name = p + 1;
5793
5794 while ((c = *++p) != '\0') {
5795 switch (c) {
5796 case CTLESC:
5797 return startp;
5798 case CTLQUOTEMARK:
5799 return startp;
5800 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005801 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005802 goto done;
5803 break;
5804 case '/':
5805 case CTLENDVAR:
5806 goto done;
5807 }
5808 }
5809 done:
5810 *p = '\0';
5811 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005812 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005813 } else {
5814 pw = getpwnam(name);
5815 if (pw == NULL)
5816 goto lose;
5817 home = pw->pw_dir;
5818 }
5819 if (!home || !*home)
5820 goto lose;
5821 *p = c;
5822 startloc = expdest - (char *)stackblock();
5823 strtodest(home, SQSYNTAX, quotes);
5824 recordregion(startloc, expdest - (char *)stackblock(), 0);
5825 return p;
5826 lose:
5827 *p = c;
5828 return startp;
5829}
5830
5831/*
5832 * Execute a command inside back quotes. If it's a builtin command, we
5833 * want to save its output in a block obtained from malloc. Otherwise
5834 * we fork off a subprocess and get the output of the command via a pipe.
5835 * Should be called with interrupts off.
5836 */
5837struct backcmd { /* result of evalbackcmd */
5838 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005839 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005840 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005841 struct job *jp; /* job structure for command */
5842};
5843
5844/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005845static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005846#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005847static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005848
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005849static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005850evalbackcmd(union node *n, struct backcmd *result)
5851{
5852 int saveherefd;
5853
5854 result->fd = -1;
5855 result->buf = NULL;
5856 result->nleft = 0;
5857 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005858 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005859 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005860
5861 saveherefd = herefd;
5862 herefd = -1;
5863
5864 {
5865 int pip[2];
5866 struct job *jp;
5867
5868 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005869 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005870 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005871 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5872 FORCE_INT_ON;
5873 close(pip[0]);
5874 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005875 /*close(1);*/
5876 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005877 close(pip[1]);
5878 }
5879 eflag = 0;
5880 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5881 /* NOTREACHED */
5882 }
5883 close(pip[1]);
5884 result->fd = pip[0];
5885 result->jp = jp;
5886 }
5887 herefd = saveherefd;
5888 out:
5889 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5890 result->fd, result->buf, result->nleft, result->jp));
5891}
5892
5893/*
5894 * Expand stuff in backwards quotes.
5895 */
5896static void
5897expbackq(union node *cmd, int quoted, int quotes)
5898{
5899 struct backcmd in;
5900 int i;
5901 char buf[128];
5902 char *p;
5903 char *dest;
5904 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005905 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005906 struct stackmark smark;
5907
5908 INT_OFF;
5909 setstackmark(&smark);
5910 dest = expdest;
5911 startloc = dest - (char *)stackblock();
5912 grabstackstr(dest);
5913 evalbackcmd(cmd, &in);
5914 popstackmark(&smark);
5915
5916 p = in.buf;
5917 i = in.nleft;
5918 if (i == 0)
5919 goto read;
5920 for (;;) {
5921 memtodest(p, i, syntax, quotes);
5922 read:
5923 if (in.fd < 0)
5924 break;
Denys Vlasenko80542ba2011-05-08 21:23:43 +02005925 i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005926 TRACE(("expbackq: read returns %d\n", i));
5927 if (i <= 0)
5928 break;
5929 p = buf;
5930 }
5931
Denis Vlasenko60818682007-09-28 22:07:23 +00005932 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005933 if (in.fd >= 0) {
5934 close(in.fd);
5935 back_exitstatus = waitforjob(in.jp);
5936 }
5937 INT_ON;
5938
5939 /* Eat all trailing newlines */
5940 dest = expdest;
5941 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5942 STUNPUTC(dest);
5943 expdest = dest;
5944
5945 if (quoted == 0)
5946 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005947 TRACE(("evalbackq: size:%d:'%.*s'\n",
5948 (int)((dest - (char *)stackblock()) - startloc),
5949 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005950 stackblock() + startloc));
5951}
5952
Mike Frysinger98c52642009-04-02 10:02:37 +00005953#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005954/*
5955 * Expand arithmetic expression. Backup to start of expression,
5956 * evaluate, place result in (backed up) result, adjust string position.
5957 */
5958static void
5959expari(int quotes)
5960{
5961 char *p, *start;
5962 int begoff;
5963 int flag;
5964 int len;
5965
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005966 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005967
5968 /*
5969 * This routine is slightly over-complicated for
5970 * efficiency. Next we scan backwards looking for the
5971 * start of arithmetic.
5972 */
5973 start = stackblock();
5974 p = expdest - 1;
5975 *p = '\0';
5976 p--;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005977 while (1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005978 int esc;
5979
Denys Vlasenkocd716832009-11-28 22:14:02 +01005980 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005981 p--;
5982#if DEBUG
5983 if (p < start) {
5984 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5985 }
5986#endif
5987 }
5988
5989 esc = esclen(start, p);
5990 if (!(esc % 2)) {
5991 break;
5992 }
5993
5994 p -= esc + 1;
Denys Vlasenko940c7202011-03-02 04:07:14 +01005995 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005996
5997 begoff = p - start;
5998
5999 removerecordregions(begoff);
6000
6001 flag = p[1];
6002
6003 expdest = p;
6004
6005 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006006 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006007
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006008 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006009
6010 if (flag != '"')
6011 recordregion(begoff, begoff + len, 0);
6012}
6013#endif
6014
6015/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006016static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006017
6018/*
6019 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6020 * characters to allow for further processing. Otherwise treat
6021 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006022 *
6023 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
6024 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
6025 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006026 */
6027static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006028argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006029{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006030 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006031 '=',
6032 ':',
6033 CTLQUOTEMARK,
6034 CTLENDVAR,
6035 CTLESC,
6036 CTLVAR,
6037 CTLBACKQ,
6038 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00006039#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006040 CTLENDARI,
6041#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006042 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006043 };
6044 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006045 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
6046 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006047 int inquotes;
6048 size_t length;
6049 int startloc;
6050
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006051 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006052 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006053 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006054 reject++;
6055 }
6056 inquotes = 0;
6057 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006058 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006059 char *q;
6060
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006061 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006062 tilde:
6063 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006064 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006065 q++;
6066 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006067 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006068 }
6069 start:
6070 startloc = expdest - (char *)stackblock();
6071 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006072 unsigned char c;
6073
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006074 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006075 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006076 if (c) {
6077 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006078 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006079 ) {
6080 /* c == '=' || c == ':' || c == CTLENDARI */
6081 length++;
6082 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006083 }
6084 if (length > 0) {
6085 int newloc;
6086 expdest = stack_nputstr(p, length, expdest);
6087 newloc = expdest - (char *)stackblock();
6088 if (breakall && !inquotes && newloc > startloc) {
6089 recordregion(startloc, newloc, 0);
6090 }
6091 startloc = newloc;
6092 }
6093 p += length + 1;
6094 length = 0;
6095
6096 switch (c) {
6097 case '\0':
6098 goto breakloop;
6099 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006100 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006101 p--;
6102 continue;
6103 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006104 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006105 reject++;
6106 /* fall through */
6107 case ':':
6108 /*
6109 * sort of a hack - expand tildes in variable
6110 * assignments (after the first '=' and after ':'s).
6111 */
6112 if (*--p == '~') {
6113 goto tilde;
6114 }
6115 continue;
6116 }
6117
6118 switch (c) {
6119 case CTLENDVAR: /* ??? */
6120 goto breakloop;
6121 case CTLQUOTEMARK:
6122 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006123 if (!inquotes
6124 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006125 && ( p[4] == (char)CTLQUOTEMARK
6126 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006127 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006128 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006129 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006130 goto start;
6131 }
6132 inquotes = !inquotes;
6133 addquote:
6134 if (quotes) {
6135 p--;
6136 length++;
6137 startloc++;
6138 }
6139 break;
6140 case CTLESC:
6141 startloc++;
6142 length++;
6143 goto addquote;
6144 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006145 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006146 goto start;
6147 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006148 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006149 case CTLBACKQ|CTLQUOTE:
6150 expbackq(argbackq->n, c, quotes);
6151 argbackq = argbackq->next;
6152 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006153#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006154 case CTLENDARI:
6155 p--;
6156 expari(quotes);
6157 goto start;
6158#endif
6159 }
6160 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006161 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006162}
6163
6164static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006165scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6166 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006167{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006168 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006169 char c;
6170
6171 loc = startp;
6172 loc2 = rmesc;
6173 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006174 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006175 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006176
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 c = *loc2;
6178 if (zero) {
6179 *loc2 = '\0';
6180 s = rmesc;
6181 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006182 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006183
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006184 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006185 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006186 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006187 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006188 loc++;
6189 loc++;
6190 loc2++;
6191 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006192 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006193}
6194
6195static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006196scanright(char *startp, char *rmesc, char *rmescend,
6197 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006198{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006199#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6200 int try2optimize = match_at_start;
6201#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006202 int esc = 0;
6203 char *loc;
6204 char *loc2;
6205
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006206 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6207 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6208 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6209 * Logic:
6210 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6211 * and on each iteration they go back two/one char until they reach the beginning.
6212 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6213 */
6214 /* TODO: document in what other circumstances we are called. */
6215
6216 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006217 int match;
6218 char c = *loc2;
6219 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006220 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006221 *loc2 = '\0';
6222 s = rmesc;
6223 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006224 match = pmatch(pattern, s);
6225 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006226 *loc2 = c;
6227 if (match)
6228 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006229#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6230 if (try2optimize) {
6231 /* Maybe we can optimize this:
6232 * if pattern ends with unescaped *, we can avoid checking
6233 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6234 * it wont match truncated "raw_value_of_" strings too.
6235 */
6236 unsigned plen = strlen(pattern);
6237 /* Does it end with "*"? */
6238 if (plen != 0 && pattern[--plen] == '*') {
6239 /* "xxxx*" is not escaped */
6240 /* "xxx\*" is escaped */
6241 /* "xx\\*" is not escaped */
6242 /* "x\\\*" is escaped */
6243 int slashes = 0;
6244 while (plen != 0 && pattern[--plen] == '\\')
6245 slashes++;
6246 if (!(slashes & 1))
6247 break; /* ends with unescaped "*" */
6248 }
6249 try2optimize = 0;
6250 }
6251#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006252 loc--;
6253 if (quotes) {
6254 if (--esc < 0) {
6255 esc = esclen(startp, loc);
6256 }
6257 if (esc % 2) {
6258 esc--;
6259 loc--;
6260 }
6261 }
6262 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006263 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006264}
6265
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006266static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006267static void
6268varunset(const char *end, const char *var, const char *umsg, int varflags)
6269{
6270 const char *msg;
6271 const char *tail;
6272
6273 tail = nullstr;
6274 msg = "parameter not set";
6275 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006276 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006277 if (varflags & VSNUL)
6278 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006279 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006280 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006281 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006282 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006283 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006284}
6285
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006286#if ENABLE_ASH_BASH_COMPAT
6287static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006288parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006289{
6290 char *idx, *repl = NULL;
6291 unsigned char c;
6292
Denys Vlasenko16149002010-08-06 22:06:21 +02006293 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006294 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006295 idx = arg;
6296 while (1) {
6297 c = *arg;
6298 if (!c)
6299 break;
6300 if (c == '/') {
6301 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006302 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006303 repl = idx + 1;
6304 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006305 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006306 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006307 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006308 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006309 /*
6310 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6311 * The result is a_\_z_c (not a\_\_z_c)!
6312 *
6313 * Enable debug prints in this function and you'll see:
6314 * ash: arg:'\\b/_\\_z_' varflags:d
6315 * ash: pattern:'\\b' repl:'_\_z_'
6316 * That is, \\b is interpreted as \\b, but \\_ as \_!
6317 * IOW: search pattern and replace string treat backslashes
6318 * differently! That is the reason why we check repl below:
6319 */
6320 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6321 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006322 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006323 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006324 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006325
6326 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006327}
6328#endif /* ENABLE_ASH_BASH_COMPAT */
6329
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006330static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006331subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006332 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006333{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006334 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006335 char *startp;
6336 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006337 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006338 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006339 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006340 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006341 int saveherefd = herefd;
6342 int amount, workloc, resetloc;
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:
Tanguy Pruvot6fef6a32012-05-05 15:26:43 +02006370 loc = str = (char*) ((uint32_t) 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);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006455 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006456
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006457#if ENABLE_ASH_BASH_COMPAT
6458 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 Vlasenko6e73af52012-07-14 01:07:39 +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;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007255 argstr(arg->narg.text, flag,
7256 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007257 p = _STPUTC('\0', expdest);
7258 expdest = p - 1;
7259 if (arglist == NULL) {
7260 return; /* here document expanded */
7261 }
7262 p = grabstackstr(p);
7263 exparg.lastp = &exparg.list;
7264 /*
7265 * TODO - EXP_REDIR
7266 */
7267 if (flag & EXP_FULL) {
7268 ifsbreakup(p, &exparg);
7269 *exparg.lastp = NULL;
7270 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007271 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007272 } else {
7273 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007274 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007275 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007276 sp->text = p;
7277 *exparg.lastp = sp;
7278 exparg.lastp = &sp->next;
7279 }
7280 if (ifsfirst.next)
7281 ifsfree();
7282 *exparg.lastp = NULL;
7283 if (exparg.list) {
7284 *arglist->lastp = exparg.list;
7285 arglist->lastp = exparg.lastp;
7286 }
7287}
7288
7289/*
7290 * Expand shell variables and backquotes inside a here document.
7291 */
7292static void
7293expandhere(union node *arg, int fd)
7294{
7295 herefd = fd;
7296 expandarg(arg, (struct arglist *)NULL, 0);
7297 full_write(fd, stackblock(), expdest - (char *)stackblock());
7298}
7299
7300/*
7301 * Returns true if the pattern matches the string.
7302 */
7303static int
7304patmatch(char *pattern, const char *string)
7305{
7306 return pmatch(preglob(pattern, 0, 0), string);
7307}
7308
7309/*
7310 * See if a pattern matches in a case statement.
7311 */
7312static int
7313casematch(union node *pattern, char *val)
7314{
7315 struct stackmark smark;
7316 int result;
7317
7318 setstackmark(&smark);
7319 argbackq = pattern->narg.backquote;
7320 STARTSTACKSTR(expdest);
7321 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007322 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7323 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007324 STACKSTRNUL(expdest);
7325 result = patmatch(stackblock(), val);
7326 popstackmark(&smark);
7327 return result;
7328}
7329
7330
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007331/* ============ find_command */
7332
7333struct builtincmd {
7334 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007335 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007336 /* unsigned flags; */
7337};
7338#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007339/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007340 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007341#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007342#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007343
7344struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007345 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007346 union param {
7347 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007348 /* index >= 0 for commands without path (slashes) */
7349 /* (TODO: what exactly does the value mean? PATH position?) */
7350 /* index == -1 for commands with slashes */
7351 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007352 const struct builtincmd *cmd;
7353 struct funcnode *func;
7354 } u;
7355};
7356/* values of cmdtype */
7357#define CMDUNKNOWN -1 /* no entry in table for command */
7358#define CMDNORMAL 0 /* command is an executable program */
7359#define CMDFUNCTION 1 /* command is a shell function */
7360#define CMDBUILTIN 2 /* command is a shell builtin */
7361
7362/* action to find_command() */
7363#define DO_ERR 0x01 /* prints errors */
7364#define DO_ABS 0x02 /* checks absolute paths */
7365#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7366#define DO_ALTPATH 0x08 /* using alternate path */
7367#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7368
7369static void find_command(char *, struct cmdentry *, int, const char *);
7370
7371
7372/* ============ Hashing commands */
7373
7374/*
7375 * When commands are first encountered, they are entered in a hash table.
7376 * This ensures that a full path search will not have to be done for them
7377 * on each invocation.
7378 *
7379 * We should investigate converting to a linear search, even though that
7380 * would make the command name "hash" a misnomer.
7381 */
7382
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007383struct tblentry {
7384 struct tblentry *next; /* next entry in hash chain */
7385 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007386 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007387 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007388 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007389};
7390
Denis Vlasenko01631112007-12-16 17:20:38 +00007391static struct tblentry **cmdtable;
7392#define INIT_G_cmdtable() do { \
7393 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7394} while (0)
7395
7396static int builtinloc = -1; /* index in path of %builtin, or -1 */
7397
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398
7399static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007400tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007401{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007402#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007403 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007404 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007405 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007406 while (*envp)
7407 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007408 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007409 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007410 /* re-exec ourselves with the new arguments */
7411 execve(bb_busybox_exec_path, argv, envp);
7412 /* If they called chroot or otherwise made the binary no longer
7413 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007414 }
7415#endif
7416
7417 repeat:
7418#ifdef SYSV
7419 do {
7420 execve(cmd, argv, envp);
7421 } while (errno == EINTR);
7422#else
7423 execve(cmd, argv, envp);
7424#endif
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007425 if (cmd == (char*) bb_busybox_exec_path) {
7426 /* We already visited ENOEXEC branch below, don't do it again */
7427//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007428 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007429 return;
7430 }
7431 if (errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007432 /* Run "cmd" as a shell script:
7433 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7434 * "If the execve() function fails with ENOEXEC, the shell
7435 * shall execute a command equivalent to having a shell invoked
7436 * with the command name as its first operand,
7437 * with any remaining arguments passed to the new shell"
7438 *
7439 * That is, do not use $SHELL, user's shell, or /bin/sh;
7440 * just call ourselves.
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02007441 *
7442 * Note that bash reads ~80 chars of the file, and if it sees
7443 * a zero byte before it sees newline, it doesn't try to
7444 * interpret it, but fails with "cannot execute binary file"
7445 * message and exit code 126. For one, this prevents attempts
7446 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007447 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007448 char **ap;
7449 char **new;
7450
7451 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007452 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007453 new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
7454 new[0] = (char*) "ash";
7455 new[1] = cmd;
7456 ap = new + 2;
7457 while ((*ap++ = *++argv) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007458 continue;
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01007459 cmd = (char*) bb_busybox_exec_path;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007460 argv = new;
7461 goto repeat;
7462 }
7463}
7464
7465/*
7466 * Exec a program. Never returns. If you change this routine, you may
7467 * have to change the find_command routine as well.
7468 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007469static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007470static void
7471shellexec(char **argv, const char *path, int idx)
7472{
7473 char *cmdname;
7474 int e;
7475 char **envp;
7476 int exerrno;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02007477 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007478
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007479 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007480 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007481 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007482#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007483 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007484#endif
7485 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007486 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02007487 if (applet_no >= 0) {
7488 /* We tried execing ourself, but it didn't work.
7489 * Maybe /proc/self/exe doesn't exist?
7490 * Try $PATH search.
7491 */
7492 goto try_PATH;
7493 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007494 e = errno;
7495 } else {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +02007496 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007497 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007498 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007499 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007500 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007501 if (errno != ENOENT && errno != ENOTDIR)
7502 e = errno;
7503 }
7504 stunalloc(cmdname);
7505 }
7506 }
7507
7508 /* Map to POSIX errors */
7509 switch (e) {
7510 case EACCES:
7511 exerrno = 126;
7512 break;
7513 case ENOENT:
7514 exerrno = 127;
7515 break;
7516 default:
7517 exerrno = 2;
7518 break;
7519 }
7520 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007521 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7522 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007523 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7524 /* NOTREACHED */
7525}
7526
7527static void
7528printentry(struct tblentry *cmdp)
7529{
7530 int idx;
7531 const char *path;
7532 char *name;
7533
7534 idx = cmdp->param.index;
7535 path = pathval();
7536 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007537 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007538 stunalloc(name);
7539 } while (--idx >= 0);
7540 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7541}
7542
7543/*
7544 * Clear out command entries. The argument specifies the first entry in
7545 * PATH which has changed.
7546 */
7547static void
7548clearcmdentry(int firstchange)
7549{
7550 struct tblentry **tblp;
7551 struct tblentry **pp;
7552 struct tblentry *cmdp;
7553
7554 INT_OFF;
7555 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7556 pp = tblp;
7557 while ((cmdp = *pp) != NULL) {
7558 if ((cmdp->cmdtype == CMDNORMAL &&
7559 cmdp->param.index >= firstchange)
7560 || (cmdp->cmdtype == CMDBUILTIN &&
7561 builtinloc >= firstchange)
7562 ) {
7563 *pp = cmdp->next;
7564 free(cmdp);
7565 } else {
7566 pp = &cmdp->next;
7567 }
7568 }
7569 }
7570 INT_ON;
7571}
7572
7573/*
7574 * Locate a command in the command hash table. If "add" is nonzero,
7575 * add the command to the table if it is not already present. The
7576 * variable "lastcmdentry" is set to point to the address of the link
7577 * pointing to the entry, so that delete_cmd_entry can delete the
7578 * entry.
7579 *
7580 * Interrupts must be off if called with add != 0.
7581 */
7582static struct tblentry **lastcmdentry;
7583
7584static struct tblentry *
7585cmdlookup(const char *name, int add)
7586{
7587 unsigned int hashval;
7588 const char *p;
7589 struct tblentry *cmdp;
7590 struct tblentry **pp;
7591
7592 p = name;
7593 hashval = (unsigned char)*p << 4;
7594 while (*p)
7595 hashval += (unsigned char)*p++;
7596 hashval &= 0x7FFF;
7597 pp = &cmdtable[hashval % CMDTABLESIZE];
7598 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7599 if (strcmp(cmdp->cmdname, name) == 0)
7600 break;
7601 pp = &cmdp->next;
7602 }
7603 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007604 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7605 + strlen(name)
7606 /* + 1 - already done because
7607 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007608 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007609 cmdp->cmdtype = CMDUNKNOWN;
7610 strcpy(cmdp->cmdname, name);
7611 }
7612 lastcmdentry = pp;
7613 return cmdp;
7614}
7615
7616/*
7617 * Delete the command entry returned on the last lookup.
7618 */
7619static void
7620delete_cmd_entry(void)
7621{
7622 struct tblentry *cmdp;
7623
7624 INT_OFF;
7625 cmdp = *lastcmdentry;
7626 *lastcmdentry = cmdp->next;
7627 if (cmdp->cmdtype == CMDFUNCTION)
7628 freefunc(cmdp->param.func);
7629 free(cmdp);
7630 INT_ON;
7631}
7632
7633/*
7634 * Add a new command entry, replacing any existing command entry for
7635 * the same name - except special builtins.
7636 */
7637static void
7638addcmdentry(char *name, struct cmdentry *entry)
7639{
7640 struct tblentry *cmdp;
7641
7642 cmdp = cmdlookup(name, 1);
7643 if (cmdp->cmdtype == CMDFUNCTION) {
7644 freefunc(cmdp->param.func);
7645 }
7646 cmdp->cmdtype = entry->cmdtype;
7647 cmdp->param = entry->u;
7648 cmdp->rehash = 0;
7649}
7650
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007651static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007652hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007653{
7654 struct tblentry **pp;
7655 struct tblentry *cmdp;
7656 int c;
7657 struct cmdentry entry;
7658 char *name;
7659
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007660 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007661 clearcmdentry(0);
7662 return 0;
7663 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007664
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007665 if (*argptr == NULL) {
7666 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7667 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7668 if (cmdp->cmdtype == CMDNORMAL)
7669 printentry(cmdp);
7670 }
7671 }
7672 return 0;
7673 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007674
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007675 c = 0;
7676 while ((name = *argptr) != NULL) {
7677 cmdp = cmdlookup(name, 0);
7678 if (cmdp != NULL
7679 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007680 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7681 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007682 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007683 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007684 find_command(name, &entry, DO_ERR, pathval());
7685 if (entry.cmdtype == CMDUNKNOWN)
7686 c = 1;
7687 argptr++;
7688 }
7689 return c;
7690}
7691
7692/*
7693 * Called when a cd is done. Marks all commands so the next time they
7694 * are executed they will be rehashed.
7695 */
7696static void
7697hashcd(void)
7698{
7699 struct tblentry **pp;
7700 struct tblentry *cmdp;
7701
7702 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7703 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007704 if (cmdp->cmdtype == CMDNORMAL
7705 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007706 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007707 && builtinloc > 0)
7708 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007709 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007710 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007711 }
7712 }
7713}
7714
7715/*
7716 * Fix command hash table when PATH changed.
7717 * Called before PATH is changed. The argument is the new value of PATH;
7718 * pathval() still returns the old value at this point.
7719 * Called with interrupts off.
7720 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007721static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007722changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007723{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007724 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007725 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007726 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007727 int idx_bltin;
7728
7729 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007730 firstchange = 9999; /* assume no change */
7731 idx = 0;
7732 idx_bltin = -1;
7733 for (;;) {
7734 if (*old != *new) {
7735 firstchange = idx;
7736 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007737 || (*old == ':' && *new == '\0')
7738 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007739 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007740 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007741 old = new; /* ignore subsequent differences */
7742 }
7743 if (*new == '\0')
7744 break;
7745 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7746 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007747 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007748 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007749 new++;
7750 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007751 }
7752 if (builtinloc < 0 && idx_bltin >= 0)
7753 builtinloc = idx_bltin; /* zap builtins */
7754 if (builtinloc >= 0 && idx_bltin < 0)
7755 firstchange = 0;
7756 clearcmdentry(firstchange);
7757 builtinloc = idx_bltin;
7758}
7759
7760#define TEOF 0
7761#define TNL 1
7762#define TREDIR 2
7763#define TWORD 3
7764#define TSEMI 4
7765#define TBACKGND 5
7766#define TAND 6
7767#define TOR 7
7768#define TPIPE 8
7769#define TLP 9
7770#define TRP 10
7771#define TENDCASE 11
7772#define TENDBQUOTE 12
7773#define TNOT 13
7774#define TCASE 14
7775#define TDO 15
7776#define TDONE 16
7777#define TELIF 17
7778#define TELSE 18
7779#define TESAC 19
7780#define TFI 20
7781#define TFOR 21
7782#define TIF 22
7783#define TIN 23
7784#define TTHEN 24
7785#define TUNTIL 25
7786#define TWHILE 26
7787#define TBEGIN 27
7788#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007789typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007790
7791/* first char is indicating which tokens mark the end of a list */
7792static const char *const tokname_array[] = {
7793 "\1end of file",
7794 "\0newline",
7795 "\0redirection",
7796 "\0word",
7797 "\0;",
7798 "\0&",
7799 "\0&&",
7800 "\0||",
7801 "\0|",
7802 "\0(",
7803 "\1)",
7804 "\1;;",
7805 "\1`",
7806#define KWDOFFSET 13
7807 /* the following are keywords */
7808 "\0!",
7809 "\0case",
7810 "\1do",
7811 "\1done",
7812 "\1elif",
7813 "\1else",
7814 "\1esac",
7815 "\1fi",
7816 "\0for",
7817 "\0if",
7818 "\0in",
7819 "\1then",
7820 "\0until",
7821 "\0while",
7822 "\0{",
7823 "\1}",
7824};
7825
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007826/* Wrapper around strcmp for qsort/bsearch/... */
7827static int
7828pstrcmp(const void *a, const void *b)
7829{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007830 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007831}
7832
7833static const char *const *
7834findkwd(const char *s)
7835{
7836 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007837 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7838 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007839}
7840
7841/*
7842 * Locate and print what a word is...
7843 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007844static int
7845describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007846{
7847 struct cmdentry entry;
7848 struct tblentry *cmdp;
7849#if ENABLE_ASH_ALIAS
7850 const struct alias *ap;
7851#endif
7852 const char *path = pathval();
7853
7854 if (describe_command_verbose) {
7855 out1str(command);
7856 }
7857
7858 /* First look at the keywords */
7859 if (findkwd(command)) {
7860 out1str(describe_command_verbose ? " is a shell keyword" : command);
7861 goto out;
7862 }
7863
7864#if ENABLE_ASH_ALIAS
7865 /* Then look at the aliases */
7866 ap = lookupalias(command, 0);
7867 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007868 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007869 out1str("alias ");
7870 printalias(ap);
7871 return 0;
7872 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007873 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874 goto out;
7875 }
7876#endif
7877 /* Then check if it is a tracked alias */
7878 cmdp = cmdlookup(command, 0);
7879 if (cmdp != NULL) {
7880 entry.cmdtype = cmdp->cmdtype;
7881 entry.u = cmdp->param;
7882 } else {
7883 /* Finally use brute force */
7884 find_command(command, &entry, DO_ABS, path);
7885 }
7886
7887 switch (entry.cmdtype) {
7888 case CMDNORMAL: {
7889 int j = entry.u.index;
7890 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007891 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007892 p = command;
7893 } else {
7894 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007895 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007896 stunalloc(p);
7897 } while (--j >= 0);
7898 }
7899 if (describe_command_verbose) {
7900 out1fmt(" is%s %s",
7901 (cmdp ? " a tracked alias for" : nullstr), p
7902 );
7903 } else {
7904 out1str(p);
7905 }
7906 break;
7907 }
7908
7909 case CMDFUNCTION:
7910 if (describe_command_verbose) {
7911 out1str(" is a shell function");
7912 } else {
7913 out1str(command);
7914 }
7915 break;
7916
7917 case CMDBUILTIN:
7918 if (describe_command_verbose) {
7919 out1fmt(" is a %sshell builtin",
7920 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7921 "special " : nullstr
7922 );
7923 } else {
7924 out1str(command);
7925 }
7926 break;
7927
7928 default:
7929 if (describe_command_verbose) {
7930 out1str(": not found\n");
7931 }
7932 return 127;
7933 }
7934 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007935 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007936 return 0;
7937}
7938
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007939static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007940typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007941{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007942 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007943 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007944 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007945
Denis Vlasenko46846e22007-05-20 13:08:31 +00007946 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007947 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007948 i++;
7949 verbose = 0;
7950 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007951 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007952 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007953 }
7954 return err;
7955}
7956
7957#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007958static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007959commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007960{
7961 int c;
7962 enum {
7963 VERIFY_BRIEF = 1,
7964 VERIFY_VERBOSE = 2,
7965 } verify = 0;
7966
7967 while ((c = nextopt("pvV")) != '\0')
7968 if (c == 'V')
7969 verify |= VERIFY_VERBOSE;
7970 else if (c == 'v')
7971 verify |= VERIFY_BRIEF;
7972#if DEBUG
7973 else if (c != 'p')
7974 abort();
7975#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007976 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7977 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007978 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007979 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007980
7981 return 0;
7982}
7983#endif
7984
7985
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007986/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007987
Denis Vlasenko340299a2008-11-21 10:36:36 +00007988static int funcblocksize; /* size of structures in function */
7989static int funcstringsize; /* size of strings in node */
7990static void *funcblock; /* block to allocate function from */
7991static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007992
Eric Andersencb57d552001-06-28 07:25:16 +00007993/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007994#define EV_EXIT 01 /* exit after evaluating tree */
7995#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007996#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007997
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007998static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007999 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8000 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8001 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8002 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8003 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8004 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8005 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8006 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8007 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8008 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8009 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8010 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8011 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8012 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8013 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8014 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8015 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008016#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008017 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008018#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008019 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8020 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8021 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8022 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8023 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8024 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8025 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8026 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8027 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008028};
8029
8030static void calcsize(union node *n);
8031
8032static void
8033sizenodelist(struct nodelist *lp)
8034{
8035 while (lp) {
8036 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8037 calcsize(lp->n);
8038 lp = lp->next;
8039 }
8040}
8041
8042static void
8043calcsize(union node *n)
8044{
8045 if (n == NULL)
8046 return;
8047 funcblocksize += nodesize[n->type];
8048 switch (n->type) {
8049 case NCMD:
8050 calcsize(n->ncmd.redirect);
8051 calcsize(n->ncmd.args);
8052 calcsize(n->ncmd.assign);
8053 break;
8054 case NPIPE:
8055 sizenodelist(n->npipe.cmdlist);
8056 break;
8057 case NREDIR:
8058 case NBACKGND:
8059 case NSUBSHELL:
8060 calcsize(n->nredir.redirect);
8061 calcsize(n->nredir.n);
8062 break;
8063 case NAND:
8064 case NOR:
8065 case NSEMI:
8066 case NWHILE:
8067 case NUNTIL:
8068 calcsize(n->nbinary.ch2);
8069 calcsize(n->nbinary.ch1);
8070 break;
8071 case NIF:
8072 calcsize(n->nif.elsepart);
8073 calcsize(n->nif.ifpart);
8074 calcsize(n->nif.test);
8075 break;
8076 case NFOR:
8077 funcstringsize += strlen(n->nfor.var) + 1;
8078 calcsize(n->nfor.body);
8079 calcsize(n->nfor.args);
8080 break;
8081 case NCASE:
8082 calcsize(n->ncase.cases);
8083 calcsize(n->ncase.expr);
8084 break;
8085 case NCLIST:
8086 calcsize(n->nclist.body);
8087 calcsize(n->nclist.pattern);
8088 calcsize(n->nclist.next);
8089 break;
8090 case NDEFUN:
8091 case NARG:
8092 sizenodelist(n->narg.backquote);
8093 funcstringsize += strlen(n->narg.text) + 1;
8094 calcsize(n->narg.next);
8095 break;
8096 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008097#if ENABLE_ASH_BASH_COMPAT
8098 case NTO2:
8099#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008100 case NCLOBBER:
8101 case NFROM:
8102 case NFROMTO:
8103 case NAPPEND:
8104 calcsize(n->nfile.fname);
8105 calcsize(n->nfile.next);
8106 break;
8107 case NTOFD:
8108 case NFROMFD:
8109 calcsize(n->ndup.vname);
8110 calcsize(n->ndup.next);
8111 break;
8112 case NHERE:
8113 case NXHERE:
8114 calcsize(n->nhere.doc);
8115 calcsize(n->nhere.next);
8116 break;
8117 case NNOT:
8118 calcsize(n->nnot.com);
8119 break;
8120 };
8121}
8122
8123static char *
8124nodeckstrdup(char *s)
8125{
8126 char *rtn = funcstring;
8127
8128 strcpy(funcstring, s);
8129 funcstring += strlen(s) + 1;
8130 return rtn;
8131}
8132
8133static union node *copynode(union node *);
8134
8135static struct nodelist *
8136copynodelist(struct nodelist *lp)
8137{
8138 struct nodelist *start;
8139 struct nodelist **lpp;
8140
8141 lpp = &start;
8142 while (lp) {
8143 *lpp = funcblock;
8144 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8145 (*lpp)->n = copynode(lp->n);
8146 lp = lp->next;
8147 lpp = &(*lpp)->next;
8148 }
8149 *lpp = NULL;
8150 return start;
8151}
8152
8153static union node *
8154copynode(union node *n)
8155{
8156 union node *new;
8157
8158 if (n == NULL)
8159 return NULL;
8160 new = funcblock;
8161 funcblock = (char *) funcblock + nodesize[n->type];
8162
8163 switch (n->type) {
8164 case NCMD:
8165 new->ncmd.redirect = copynode(n->ncmd.redirect);
8166 new->ncmd.args = copynode(n->ncmd.args);
8167 new->ncmd.assign = copynode(n->ncmd.assign);
8168 break;
8169 case NPIPE:
8170 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008171 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008172 break;
8173 case NREDIR:
8174 case NBACKGND:
8175 case NSUBSHELL:
8176 new->nredir.redirect = copynode(n->nredir.redirect);
8177 new->nredir.n = copynode(n->nredir.n);
8178 break;
8179 case NAND:
8180 case NOR:
8181 case NSEMI:
8182 case NWHILE:
8183 case NUNTIL:
8184 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8185 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8186 break;
8187 case NIF:
8188 new->nif.elsepart = copynode(n->nif.elsepart);
8189 new->nif.ifpart = copynode(n->nif.ifpart);
8190 new->nif.test = copynode(n->nif.test);
8191 break;
8192 case NFOR:
8193 new->nfor.var = nodeckstrdup(n->nfor.var);
8194 new->nfor.body = copynode(n->nfor.body);
8195 new->nfor.args = copynode(n->nfor.args);
8196 break;
8197 case NCASE:
8198 new->ncase.cases = copynode(n->ncase.cases);
8199 new->ncase.expr = copynode(n->ncase.expr);
8200 break;
8201 case NCLIST:
8202 new->nclist.body = copynode(n->nclist.body);
8203 new->nclist.pattern = copynode(n->nclist.pattern);
8204 new->nclist.next = copynode(n->nclist.next);
8205 break;
8206 case NDEFUN:
8207 case NARG:
8208 new->narg.backquote = copynodelist(n->narg.backquote);
8209 new->narg.text = nodeckstrdup(n->narg.text);
8210 new->narg.next = copynode(n->narg.next);
8211 break;
8212 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008213#if ENABLE_ASH_BASH_COMPAT
8214 case NTO2:
8215#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008216 case NCLOBBER:
8217 case NFROM:
8218 case NFROMTO:
8219 case NAPPEND:
8220 new->nfile.fname = copynode(n->nfile.fname);
8221 new->nfile.fd = n->nfile.fd;
8222 new->nfile.next = copynode(n->nfile.next);
8223 break;
8224 case NTOFD:
8225 case NFROMFD:
8226 new->ndup.vname = copynode(n->ndup.vname);
8227 new->ndup.dupfd = n->ndup.dupfd;
8228 new->ndup.fd = n->ndup.fd;
8229 new->ndup.next = copynode(n->ndup.next);
8230 break;
8231 case NHERE:
8232 case NXHERE:
8233 new->nhere.doc = copynode(n->nhere.doc);
8234 new->nhere.fd = n->nhere.fd;
8235 new->nhere.next = copynode(n->nhere.next);
8236 break;
8237 case NNOT:
8238 new->nnot.com = copynode(n->nnot.com);
8239 break;
8240 };
8241 new->type = n->type;
8242 return new;
8243}
8244
8245/*
8246 * Make a copy of a parse tree.
8247 */
8248static struct funcnode *
8249copyfunc(union node *n)
8250{
8251 struct funcnode *f;
8252 size_t blocksize;
8253
8254 funcblocksize = offsetof(struct funcnode, n);
8255 funcstringsize = 0;
8256 calcsize(n);
8257 blocksize = funcblocksize;
8258 f = ckmalloc(blocksize + funcstringsize);
8259 funcblock = (char *) f + offsetof(struct funcnode, n);
8260 funcstring = (char *) f + blocksize;
8261 copynode(n);
8262 f->count = 0;
8263 return f;
8264}
8265
8266/*
8267 * Define a shell function.
8268 */
8269static void
8270defun(char *name, union node *func)
8271{
8272 struct cmdentry entry;
8273
8274 INT_OFF;
8275 entry.cmdtype = CMDFUNCTION;
8276 entry.u.func = copyfunc(func);
8277 addcmdentry(name, &entry);
8278 INT_ON;
8279}
8280
Denis Vlasenko4b875702009-03-19 13:30:04 +00008281/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008282#define SKIPBREAK (1 << 0)
8283#define SKIPCONT (1 << 1)
8284#define SKIPFUNC (1 << 2)
8285#define SKIPFILE (1 << 3)
8286#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008287static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008288static int skipcount; /* number of levels to skip */
8289static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008290static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008291
Denis Vlasenko4b875702009-03-19 13:30:04 +00008292/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008293static int evalstring(char *s, int mask);
8294
Denis Vlasenko4b875702009-03-19 13:30:04 +00008295/* Called to execute a trap.
8296 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008297 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008298 *
8299 * Perhaps we should avoid entering new trap handlers
8300 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008301 */
8302static int
8303dotrap(void)
8304{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008305 uint8_t *g;
8306 int sig;
8307 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008308
8309 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008310 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008311 xbarrier();
8312
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008313 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008314 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8315 int want_exexit;
8316 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008317
Denis Vlasenko4b875702009-03-19 13:30:04 +00008318 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008319 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008320 t = trap[sig];
8321 /* non-trapped SIGINT is handled separately by raise_interrupt,
8322 * don't upset it by resetting gotsig[SIGINT-1] */
8323 if (sig == SIGINT && !t)
8324 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008325
8326 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008327 *g = 0;
8328 if (!t)
8329 continue;
8330 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008331 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008332 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008333 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008334 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008335 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008336 }
8337
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008338 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008339 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008340}
8341
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008342/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008343static void evalloop(union node *, int);
8344static void evalfor(union node *, int);
8345static void evalcase(union node *, int);
8346static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008347static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008348static void evalpipe(union node *, int);
8349static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008350static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008351static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008352
Eric Andersen62483552001-07-10 06:09:16 +00008353/*
Eric Andersenc470f442003-07-28 09:56:35 +00008354 * Evaluate a parse tree. The value is left in the global variable
8355 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008356 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008357static void
Eric Andersenc470f442003-07-28 09:56:35 +00008358evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008359{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008360 struct jmploc *volatile savehandler = exception_handler;
8361 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008362 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008363 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008364 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008365 int int_level;
8366
8367 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008368
Eric Andersenc470f442003-07-28 09:56:35 +00008369 if (n == NULL) {
8370 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008371 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008372 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008373 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008374
8375 exception_handler = &jmploc;
8376 {
8377 int err = setjmp(jmploc.loc);
8378 if (err) {
8379 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008380 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008381 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8382 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008383 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008384 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008385 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008386 TRACE(("exception %d in evaltree, propagating err=%d\n",
8387 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008388 exception_handler = savehandler;
8389 longjmp(exception_handler->loc, err);
8390 }
8391 }
8392
Eric Andersenc470f442003-07-28 09:56:35 +00008393 switch (n->type) {
8394 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008395#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008396 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008397 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008398 break;
8399#endif
8400 case NNOT:
8401 evaltree(n->nnot.com, EV_TESTED);
8402 status = !exitstatus;
8403 goto setstatus;
8404 case NREDIR:
8405 expredir(n->nredir.redirect);
8406 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8407 if (!status) {
8408 evaltree(n->nredir.n, flags & EV_TESTED);
8409 status = exitstatus;
8410 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008411 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008412 goto setstatus;
8413 case NCMD:
8414 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008415 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008416 if (eflag && !(flags & EV_TESTED))
8417 checkexit = ~0;
8418 goto calleval;
8419 case NFOR:
8420 evalfn = evalfor;
8421 goto calleval;
8422 case NWHILE:
8423 case NUNTIL:
8424 evalfn = evalloop;
8425 goto calleval;
8426 case NSUBSHELL:
8427 case NBACKGND:
8428 evalfn = evalsubshell;
8429 goto calleval;
8430 case NPIPE:
8431 evalfn = evalpipe;
8432 goto checkexit;
8433 case NCASE:
8434 evalfn = evalcase;
8435 goto calleval;
8436 case NAND:
8437 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008438 case NSEMI: {
8439
Eric Andersenc470f442003-07-28 09:56:35 +00008440#if NAND + 1 != NOR
8441#error NAND + 1 != NOR
8442#endif
8443#if NOR + 1 != NSEMI
8444#error NOR + 1 != NSEMI
8445#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008446 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008447 evaltree(
8448 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008449 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008450 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008451 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008452 break;
8453 if (!evalskip) {
8454 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008455 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008456 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008457 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008458 evalfn(n, flags);
8459 break;
8460 }
8461 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008462 }
Eric Andersenc470f442003-07-28 09:56:35 +00008463 case NIF:
8464 evaltree(n->nif.test, EV_TESTED);
8465 if (evalskip)
8466 break;
8467 if (exitstatus == 0) {
8468 n = n->nif.ifpart;
8469 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008470 }
8471 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008472 n = n->nif.elsepart;
8473 goto evaln;
8474 }
8475 goto success;
8476 case NDEFUN:
8477 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008478 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008479 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008480 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008481 exitstatus = status;
8482 break;
8483 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008484
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008485 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008486 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008487
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008488 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008489 /* Order of checks below is important:
8490 * signal handlers trigger before exit caused by "set -e".
8491 */
8492 if (pending_sig && dotrap())
8493 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008494 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008495 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008496
8497 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008498 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008499 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008500 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008501
8502 RESTORE_INT(int_level);
8503 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008504}
8505
Eric Andersenc470f442003-07-28 09:56:35 +00008506#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8507static
8508#endif
8509void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8510
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008511static void
Eric Andersenc470f442003-07-28 09:56:35 +00008512evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008513{
8514 int status;
8515
8516 loopnest++;
8517 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008518 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008519 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008520 int i;
8521
Eric Andersencb57d552001-06-28 07:25:16 +00008522 evaltree(n->nbinary.ch1, EV_TESTED);
8523 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008524 skipping:
8525 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008526 evalskip = 0;
8527 continue;
8528 }
8529 if (evalskip == SKIPBREAK && --skipcount <= 0)
8530 evalskip = 0;
8531 break;
8532 }
Eric Andersenc470f442003-07-28 09:56:35 +00008533 i = exitstatus;
8534 if (n->type != NWHILE)
8535 i = !i;
8536 if (i != 0)
8537 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008538 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008539 status = exitstatus;
8540 if (evalskip)
8541 goto skipping;
8542 }
8543 loopnest--;
8544 exitstatus = status;
8545}
8546
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008547static void
Eric Andersenc470f442003-07-28 09:56:35 +00008548evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008549{
8550 struct arglist arglist;
8551 union node *argp;
8552 struct strlist *sp;
8553 struct stackmark smark;
8554
8555 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008556 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008557 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008558 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008559 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008560 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008561 if (evalskip)
8562 goto out;
8563 }
8564 *arglist.lastp = NULL;
8565
8566 exitstatus = 0;
8567 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008568 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008569 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008570 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008571 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008572 if (evalskip) {
8573 if (evalskip == SKIPCONT && --skipcount <= 0) {
8574 evalskip = 0;
8575 continue;
8576 }
8577 if (evalskip == SKIPBREAK && --skipcount <= 0)
8578 evalskip = 0;
8579 break;
8580 }
8581 }
8582 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008583 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008584 popstackmark(&smark);
8585}
8586
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008587static void
Eric Andersenc470f442003-07-28 09:56:35 +00008588evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008589{
8590 union node *cp;
8591 union node *patp;
8592 struct arglist arglist;
8593 struct stackmark smark;
8594
8595 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008596 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008597 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008598 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008599 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008600 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8601 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008602 if (casematch(patp, arglist.list->text)) {
8603 if (evalskip == 0) {
8604 evaltree(cp->nclist.body, flags);
8605 }
8606 goto out;
8607 }
8608 }
8609 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008610 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008611 popstackmark(&smark);
8612}
8613
Eric Andersenc470f442003-07-28 09:56:35 +00008614/*
8615 * Kick off a subshell to evaluate a tree.
8616 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008617static void
Eric Andersenc470f442003-07-28 09:56:35 +00008618evalsubshell(union node *n, int flags)
8619{
8620 struct job *jp;
8621 int backgnd = (n->type == NBACKGND);
8622 int status;
8623
8624 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008625 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008626 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008627 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008628 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008629 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008630 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008631 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008632 flags |= EV_EXIT;
8633 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008634 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008635 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008636 redirect(n->nredir.redirect, 0);
8637 evaltreenr(n->nredir.n, flags);
8638 /* never returns */
8639 }
8640 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008641 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008642 status = waitforjob(jp);
8643 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008644 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008645}
8646
Eric Andersenc470f442003-07-28 09:56:35 +00008647/*
8648 * Compute the names of the files in a redirection list.
8649 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008650static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008651static void
8652expredir(union node *n)
8653{
8654 union node *redir;
8655
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008656 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008657 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008658
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008659 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008660 fn.lastp = &fn.list;
8661 switch (redir->type) {
8662 case NFROMTO:
8663 case NFROM:
8664 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008665#if ENABLE_ASH_BASH_COMPAT
8666 case NTO2:
8667#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008668 case NCLOBBER:
8669 case NAPPEND:
8670 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008671#if ENABLE_ASH_BASH_COMPAT
8672 store_expfname:
8673#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008674 redir->nfile.expfname = fn.list->text;
8675 break;
8676 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008677 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008678 if (redir->ndup.vname) {
8679 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008680 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008681 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008682#if ENABLE_ASH_BASH_COMPAT
8683//FIXME: we used expandarg with different args!
8684 if (!isdigit_str9(fn.list->text)) {
8685 /* >&file, not >&fd */
8686 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8687 ash_msg_and_raise_error("redir error");
8688 redir->type = NTO2;
8689 goto store_expfname;
8690 }
8691#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008692 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008693 }
8694 break;
8695 }
8696 }
8697}
8698
Eric Andersencb57d552001-06-28 07:25:16 +00008699/*
Eric Andersencb57d552001-06-28 07:25:16 +00008700 * Evaluate a pipeline. All the processes in the pipeline are children
8701 * of the process creating the pipeline. (This differs from some versions
8702 * of the shell, which make the last process in a pipeline the parent
8703 * of all the rest.)
8704 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008705static void
Eric Andersenc470f442003-07-28 09:56:35 +00008706evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008707{
8708 struct job *jp;
8709 struct nodelist *lp;
8710 int pipelen;
8711 int prevfd;
8712 int pip[2];
8713
Eric Andersenc470f442003-07-28 09:56:35 +00008714 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008715 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008716 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008717 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008718 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008719 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008720 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008721 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008722 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008723 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008724 pip[1] = -1;
8725 if (lp->next) {
8726 if (pipe(pip) < 0) {
8727 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008728 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008729 }
8730 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008731 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008732 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008733 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008734 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008735 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008736 if (prevfd > 0) {
8737 dup2(prevfd, 0);
8738 close(prevfd);
8739 }
8740 if (pip[1] > 1) {
8741 dup2(pip[1], 1);
8742 close(pip[1]);
8743 }
Eric Andersenc470f442003-07-28 09:56:35 +00008744 evaltreenr(lp->n, flags);
8745 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008746 }
8747 if (prevfd >= 0)
8748 close(prevfd);
8749 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008750 /* Don't want to trigger debugging */
8751 if (pip[1] != -1)
8752 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008753 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008754 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008755 exitstatus = waitforjob(jp);
8756 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008757 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008758 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008759}
8760
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008761/*
8762 * Controls whether the shell is interactive or not.
8763 */
8764static void
8765setinteractive(int on)
8766{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008767 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008768
8769 if (++on == is_interactive)
8770 return;
8771 is_interactive = on;
8772 setsignal(SIGINT);
8773 setsignal(SIGQUIT);
8774 setsignal(SIGTERM);
8775#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8776 if (is_interactive > 1) {
8777 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008778 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008779
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008780 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008781 /* note: ash and hush share this string */
8782 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008783 "Enter 'help' for a list of built-in commands."
8784 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008785 bb_banner,
8786 "built-in shell (ash)"
8787 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008788 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008789 }
8790 }
8791#endif
8792}
8793
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008794static void
8795optschanged(void)
8796{
8797#if DEBUG
8798 opentrace();
8799#endif
8800 setinteractive(iflag);
8801 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008802#if ENABLE_FEATURE_EDITING_VI
8803 if (viflag)
8804 line_input_state->flags |= VI_MODE;
8805 else
8806 line_input_state->flags &= ~VI_MODE;
8807#else
8808 viflag = 0; /* forcibly keep the option off */
8809#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008810}
8811
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008812static struct localvar *localvars;
8813
8814/*
8815 * Called after a function returns.
8816 * Interrupts must be off.
8817 */
8818static void
8819poplocalvars(void)
8820{
8821 struct localvar *lvp;
8822 struct var *vp;
8823
8824 while ((lvp = localvars) != NULL) {
8825 localvars = lvp->next;
8826 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008827 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008828 if (vp == NULL) { /* $- saved */
8829 memcpy(optlist, lvp->text, sizeof(optlist));
8830 free((char*)lvp->text);
8831 optschanged();
8832 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008833 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008834 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008835 if (vp->var_func)
8836 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008837 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008838 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008839 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008840 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008841 }
8842 free(lvp);
8843 }
8844}
8845
8846static int
8847evalfun(struct funcnode *func, int argc, char **argv, int flags)
8848{
8849 volatile struct shparam saveparam;
8850 struct localvar *volatile savelocalvars;
8851 struct jmploc *volatile savehandler;
8852 struct jmploc jmploc;
8853 int e;
8854
8855 saveparam = shellparam;
8856 savelocalvars = localvars;
8857 e = setjmp(jmploc.loc);
8858 if (e) {
8859 goto funcdone;
8860 }
8861 INT_OFF;
8862 savehandler = exception_handler;
8863 exception_handler = &jmploc;
8864 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008865 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008866 func->count++;
8867 funcnest++;
8868 INT_ON;
8869 shellparam.nparam = argc - 1;
8870 shellparam.p = argv + 1;
8871#if ENABLE_ASH_GETOPTS
8872 shellparam.optind = 1;
8873 shellparam.optoff = -1;
8874#endif
8875 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008876 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008877 INT_OFF;
8878 funcnest--;
8879 freefunc(func);
8880 poplocalvars();
8881 localvars = savelocalvars;
8882 freeparam(&shellparam);
8883 shellparam = saveparam;
8884 exception_handler = savehandler;
8885 INT_ON;
8886 evalskip &= ~SKIPFUNC;
8887 return e;
8888}
8889
Denis Vlasenko131ae172007-02-18 13:00:19 +00008890#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008891static char **
8892parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008893{
8894 char *cp, c;
8895
8896 for (;;) {
8897 cp = *++argv;
8898 if (!cp)
8899 return 0;
8900 if (*cp++ != '-')
8901 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008902 c = *cp++;
8903 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008904 break;
8905 if (c == '-' && !*cp) {
8906 argv++;
8907 break;
8908 }
8909 do {
8910 switch (c) {
8911 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008912 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008913 break;
8914 default:
8915 /* run 'typecmd' for other options */
8916 return 0;
8917 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008918 c = *cp++;
8919 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008920 }
8921 return argv;
8922}
8923#endif
8924
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008925/*
8926 * Make a variable a local variable. When a variable is made local, it's
8927 * value and flags are saved in a localvar structure. The saved values
8928 * will be restored when the shell function returns. We handle the name
8929 * "-" as a special case.
8930 */
8931static void
8932mklocal(char *name)
8933{
8934 struct localvar *lvp;
8935 struct var **vpp;
8936 struct var *vp;
8937
8938 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008939 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008940 if (LONE_DASH(name)) {
8941 char *p;
8942 p = ckmalloc(sizeof(optlist));
8943 lvp->text = memcpy(p, optlist, sizeof(optlist));
8944 vp = NULL;
8945 } else {
8946 char *eq;
8947
8948 vpp = hashvar(name);
8949 vp = *findvar(vpp, name);
8950 eq = strchr(name, '=');
8951 if (vp == NULL) {
8952 if (eq)
8953 setvareq(name, VSTRFIXED);
8954 else
8955 setvar(name, NULL, VSTRFIXED);
8956 vp = *vpp; /* the new variable */
8957 lvp->flags = VUNSET;
8958 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008959 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008960 lvp->flags = vp->flags;
8961 vp->flags |= VSTRFIXED|VTEXTFIXED;
8962 if (eq)
8963 setvareq(name, 0);
8964 }
8965 }
8966 lvp->vp = vp;
8967 lvp->next = localvars;
8968 localvars = lvp;
8969 INT_ON;
8970}
8971
8972/*
8973 * The "local" command.
8974 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008975static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008976localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008977{
8978 char *name;
8979
8980 argv = argptr;
8981 while ((name = *argv++) != NULL) {
8982 mklocal(name);
8983 }
8984 return 0;
8985}
8986
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008987static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008988falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008989{
8990 return 1;
8991}
8992
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008993static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008994truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008995{
8996 return 0;
8997}
8998
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008999static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009000execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009001{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009002 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009003 iflag = 0; /* exit on error */
9004 mflag = 0;
9005 optschanged();
9006 shellexec(argv + 1, pathval(), 0);
9007 }
9008 return 0;
9009}
9010
9011/*
9012 * The return command.
9013 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009014static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009015returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009016{
9017 /*
9018 * If called outside a function, do what ksh does;
9019 * skip the rest of the file.
9020 */
9021 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
9022 return argv[1] ? number(argv[1]) : exitstatus;
9023}
9024
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009025/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009026static int breakcmd(int, char **) FAST_FUNC;
9027static int dotcmd(int, char **) FAST_FUNC;
9028static int evalcmd(int, char **) FAST_FUNC;
9029static int exitcmd(int, char **) FAST_FUNC;
9030static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009031#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009032static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009033#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00009034#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009035static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009036#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009037#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009038static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009039#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009040static int readcmd(int, char **) FAST_FUNC;
9041static int setcmd(int, char **) FAST_FUNC;
9042static int shiftcmd(int, char **) FAST_FUNC;
9043static int timescmd(int, char **) FAST_FUNC;
9044static int trapcmd(int, char **) FAST_FUNC;
9045static int umaskcmd(int, char **) FAST_FUNC;
9046static int unsetcmd(int, char **) FAST_FUNC;
9047static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009048
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009049#define BUILTIN_NOSPEC "0"
9050#define BUILTIN_SPECIAL "1"
9051#define BUILTIN_REGULAR "2"
9052#define BUILTIN_SPEC_REG "3"
9053#define BUILTIN_ASSIGN "4"
9054#define BUILTIN_SPEC_ASSG "5"
9055#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009056#define BUILTIN_SPEC_REG_ASSG "7"
9057
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009058/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009059#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009060static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009061#endif
9062#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009063static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009064#endif
9065#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009066static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009067#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009068
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009069/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_SPEC_REG "." , dotcmd },
9072 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009073#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009074 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009075#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009076 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009077#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009078#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009079#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009080 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009081#endif
9082#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009083 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009084#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009085 { BUILTIN_SPEC_REG "break" , breakcmd },
9086 { BUILTIN_REGULAR "cd" , cdcmd },
9087 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009088#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009089 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009090#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009091 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009092#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009093 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009094#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009095 { BUILTIN_SPEC_REG "eval" , evalcmd },
9096 { BUILTIN_SPEC_REG "exec" , execcmd },
9097 { BUILTIN_SPEC_REG "exit" , exitcmd },
9098 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9099 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009100#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009101 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009102#endif
9103#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009104 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009105#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009106 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009107#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009108 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009109#endif
9110#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009111 { BUILTIN_REGULAR "jobs" , jobscmd },
9112 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009113#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009114#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009115 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009116#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009117 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009118#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009119 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009120#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009121 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9122 { BUILTIN_REGULAR "read" , readcmd },
9123 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9124 { BUILTIN_SPEC_REG "return" , returncmd },
9125 { BUILTIN_SPEC_REG "set" , setcmd },
9126 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009127#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009128 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009129#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009130#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009131 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009132#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009133 { BUILTIN_SPEC_REG "times" , timescmd },
9134 { BUILTIN_SPEC_REG "trap" , trapcmd },
9135 { BUILTIN_REGULAR "true" , truecmd },
9136 { BUILTIN_NOSPEC "type" , typecmd },
9137 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9138 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009139#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009140 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009141#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009142 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9143 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009144};
9145
Denis Vlasenko80591b02008-03-25 07:49:43 +00009146/* Should match the above table! */
9147#define COMMANDCMD (builtintab + \
9148 2 + \
9149 1 * ENABLE_ASH_BUILTIN_TEST + \
9150 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9151 1 * ENABLE_ASH_ALIAS + \
9152 1 * ENABLE_ASH_JOB_CONTROL + \
9153 3)
9154#define EXECCMD (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 1 * ENABLE_ASH_CMDCMD + \
9162 1 + \
9163 ENABLE_ASH_BUILTIN_ECHO + \
9164 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009165
9166/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009167 * Search the table of builtin commands.
9168 */
9169static struct builtincmd *
9170find_builtin(const char *name)
9171{
9172 struct builtincmd *bp;
9173
9174 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009175 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009176 pstrcmp
9177 );
9178 return bp;
9179}
9180
9181/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009182 * Execute a simple command.
9183 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009184static int
9185isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009186{
9187 const char *q = endofname(p);
9188 if (p == q)
9189 return 0;
9190 return *q == '=';
9191}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009192static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009193bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009194{
9195 /* Preserve exitstatus of a previous possible redirection
9196 * as POSIX mandates */
9197 return back_exitstatus;
9198}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009199static void
Eric Andersenc470f442003-07-28 09:56:35 +00009200evalcommand(union node *cmd, int flags)
9201{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009202 static const struct builtincmd null_bltin = {
9203 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009204 };
Eric Andersenc470f442003-07-28 09:56:35 +00009205 struct stackmark smark;
9206 union node *argp;
9207 struct arglist arglist;
9208 struct arglist varlist;
9209 char **argv;
9210 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009211 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009212 struct cmdentry cmdentry;
9213 struct job *jp;
9214 char *lastarg;
9215 const char *path;
9216 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009217 int status;
9218 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009219 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009220 smallint cmd_is_exec;
9221 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009222
9223 /* First expand the arguments. */
9224 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9225 setstackmark(&smark);
9226 back_exitstatus = 0;
9227
9228 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009229 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009230 varlist.lastp = &varlist.list;
9231 *varlist.lastp = NULL;
9232 arglist.lastp = &arglist.list;
9233 *arglist.lastp = NULL;
9234
9235 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009236 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009237 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9238 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9239 }
9240
Eric Andersenc470f442003-07-28 09:56:35 +00009241 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9242 struct strlist **spp;
9243
9244 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009245 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009246 expandarg(argp, &arglist, EXP_VARTILDE);
9247 else
9248 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9249
Eric Andersenc470f442003-07-28 09:56:35 +00009250 for (sp = *spp; sp; sp = sp->next)
9251 argc++;
9252 }
9253
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009254 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009255 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009256 TRACE(("evalcommand arg: %s\n", sp->text));
9257 *nargv++ = sp->text;
9258 }
9259 *nargv = NULL;
9260
9261 lastarg = NULL;
9262 if (iflag && funcnest == 0 && argc > 0)
9263 lastarg = nargv[-1];
9264
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009265 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009266 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009267 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009268
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009269 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009270 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9271 struct strlist **spp;
9272 char *p;
9273
9274 spp = varlist.lastp;
9275 expandarg(argp, &varlist, EXP_VARTILDE);
9276
9277 /*
9278 * Modify the command lookup path, if a PATH= assignment
9279 * is present
9280 */
9281 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009282 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009283 path = p;
9284 }
9285
9286 /* Print the command if xflag is set. */
9287 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009288 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009289 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009290
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009291 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009292 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009293 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009294 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009295 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009296 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009297 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009298 }
9299 sp = arglist.list;
9300 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009301 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009302 }
9303
9304 cmd_is_exec = 0;
9305 spclbltin = -1;
9306
9307 /* Now locate the command. */
9308 if (argc) {
9309 const char *oldpath;
9310 int cmd_flag = DO_ERR;
9311
9312 path += 5;
9313 oldpath = path;
9314 for (;;) {
9315 find_command(argv[0], &cmdentry, cmd_flag, path);
9316 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009317 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009318 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009319 goto bail;
9320 }
9321
9322 /* implement bltin and command here */
9323 if (cmdentry.cmdtype != CMDBUILTIN)
9324 break;
9325 if (spclbltin < 0)
9326 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9327 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009328 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009329#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009330 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009331 path = oldpath;
9332 nargv = parse_command_args(argv, &path);
9333 if (!nargv)
9334 break;
9335 argc -= nargv - argv;
9336 argv = nargv;
9337 cmd_flag |= DO_NOFUNC;
9338 } else
9339#endif
9340 break;
9341 }
9342 }
9343
9344 if (status) {
9345 /* We have a redirection error. */
9346 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009347 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009348 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009349 exitstatus = status;
9350 goto out;
9351 }
9352
9353 /* Execute the command. */
9354 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009355 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009356
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009357#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009358/* (1) BUG: if variables are set, we need to fork, or save/restore them
9359 * around run_nofork_applet() call.
9360 * (2) Should this check also be done in forkshell()?
9361 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9362 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009363 /* find_command() encodes applet_no as (-2 - applet_no) */
9364 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009365 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009366 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009367 /* run <applet>_main() */
9368 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009369 break;
9370 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009371#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009372 /* Can we avoid forking off? For example, very last command
9373 * in a script or a subshell does not need forking,
9374 * we can just exec it.
9375 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009376 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009377 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009378 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009379 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009380 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009381 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009382 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009383 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009384 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009385 break;
9386 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009387 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009388 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009389 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009390 }
9391 listsetvar(varlist.list, VEXPORT|VSTACK);
9392 shellexec(argv, path, cmdentry.u.index);
9393 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009394 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009395 case CMDBUILTIN:
9396 cmdenviron = varlist.list;
9397 if (cmdenviron) {
9398 struct strlist *list = cmdenviron;
9399 int i = VNOSET;
9400 if (spclbltin > 0 || argc == 0) {
9401 i = 0;
9402 if (cmd_is_exec && argc > 1)
9403 i = VEXPORT;
9404 }
9405 listsetvar(list, i);
9406 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009407 /* Tight loop with builtins only:
9408 * "while kill -0 $child; do true; done"
9409 * will never exit even if $child died, unless we do this
9410 * to reap the zombie and make kill detect that it's gone: */
9411 dowait(DOWAIT_NONBLOCK, NULL);
9412
Eric Andersenc470f442003-07-28 09:56:35 +00009413 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9414 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009415 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009416 if (i == EXEXIT)
9417 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009418 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009419 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009420 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009421 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009422 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009423 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009424 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009425 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009426 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009427 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009428 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009429 }
9430 break;
9431
9432 case CMDFUNCTION:
9433 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009434 /* See above for the rationale */
9435 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009436 if (evalfun(cmdentry.u.func, argc, argv, flags))
9437 goto raise;
9438 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009439
9440 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009441
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009442 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009443 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009444 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009445 /* dsl: I think this is intended to be used to support
9446 * '_' in 'vi' command mode during line editing...
9447 * However I implemented that within libedit itself.
9448 */
9449 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009450 }
Eric Andersenc470f442003-07-28 09:56:35 +00009451 popstackmark(&smark);
9452}
9453
9454static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009455evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9456{
Eric Andersenc470f442003-07-28 09:56:35 +00009457 char *volatile savecmdname;
9458 struct jmploc *volatile savehandler;
9459 struct jmploc jmploc;
9460 int i;
9461
9462 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009463 i = setjmp(jmploc.loc);
9464 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009465 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009466 savehandler = exception_handler;
9467 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009468 commandname = argv[0];
9469 argptr = argv + 1;
9470 optptr = NULL; /* initialize nextopt */
9471 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009472 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009473 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009474 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009475 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009476 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009477 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009478
9479 return i;
9480}
9481
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009482static int
9483goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009484{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009485 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009486}
9487
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009488
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009489/*
9490 * Search for a command. This is called before we fork so that the
9491 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009492 * the child. The check for "goodname" is an overly conservative
9493 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009494 */
Eric Andersenc470f442003-07-28 09:56:35 +00009495static void
9496prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009497{
9498 struct cmdentry entry;
9499
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009500 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9501 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009502}
9503
Eric Andersencb57d552001-06-28 07:25:16 +00009504
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009505/* ============ Builtin commands
9506 *
9507 * Builtin commands whose functions are closely tied to evaluation
9508 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009509 */
9510
9511/*
Eric Andersencb57d552001-06-28 07:25:16 +00009512 * Handle break and continue commands. Break, continue, and return are
9513 * all handled by setting the evalskip flag. The evaluation routines
9514 * above all check this flag, and if it is set they start skipping
9515 * commands rather than executing them. The variable skipcount is
9516 * the number of loops to break/continue, or the number of function
9517 * levels to return. (The latter is always 1.) It should probably
9518 * be an error to break out of more loops than exist, but it isn't
9519 * in the standard shell so we don't make it one here.
9520 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009521static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009522breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009523{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009524 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009525
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009526 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009527 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009528 if (n > loopnest)
9529 n = loopnest;
9530 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009531 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009532 skipcount = n;
9533 }
9534 return 0;
9535}
9536
Eric Andersenc470f442003-07-28 09:56:35 +00009537
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009538/* ============ input.c
9539 *
Eric Andersen90898442003-08-06 11:20:52 +00009540 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009541 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009542
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009543enum {
9544 INPUT_PUSH_FILE = 1,
9545 INPUT_NOFILE_OK = 2,
9546};
Eric Andersencb57d552001-06-28 07:25:16 +00009547
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009548static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009549/* values of checkkwd variable */
9550#define CHKALIAS 0x1
9551#define CHKKWD 0x2
9552#define CHKNL 0x4
9553
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009554/*
9555 * Push a string back onto the input at this current parsefile level.
9556 * We handle aliases this way.
9557 */
9558#if !ENABLE_ASH_ALIAS
9559#define pushstring(s, ap) pushstring(s)
9560#endif
9561static void
9562pushstring(char *s, struct alias *ap)
9563{
9564 struct strpush *sp;
9565 int len;
9566
9567 len = strlen(s);
9568 INT_OFF;
9569 if (g_parsefile->strpush) {
9570 sp = ckzalloc(sizeof(*sp));
9571 sp->prev = g_parsefile->strpush;
9572 } else {
9573 sp = &(g_parsefile->basestrpush);
9574 }
9575 g_parsefile->strpush = sp;
9576 sp->prev_string = g_parsefile->next_to_pgetc;
9577 sp->prev_left_in_line = g_parsefile->left_in_line;
9578#if ENABLE_ASH_ALIAS
9579 sp->ap = ap;
9580 if (ap) {
9581 ap->flag |= ALIASINUSE;
9582 sp->string = s;
9583 }
9584#endif
9585 g_parsefile->next_to_pgetc = s;
9586 g_parsefile->left_in_line = len;
9587 INT_ON;
9588}
9589
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009590static void
9591popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009592{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009593 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009594
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009595 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009596#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009597 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009598 if (g_parsefile->next_to_pgetc[-1] == ' '
9599 || g_parsefile->next_to_pgetc[-1] == '\t'
9600 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009601 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009602 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009603 if (sp->string != sp->ap->val) {
9604 free(sp->string);
9605 }
9606 sp->ap->flag &= ~ALIASINUSE;
9607 if (sp->ap->flag & ALIASDEAD) {
9608 unalias(sp->ap->name);
9609 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009610 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009611#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009612 g_parsefile->next_to_pgetc = sp->prev_string;
9613 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009614 g_parsefile->strpush = sp->prev;
9615 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009616 free(sp);
9617 INT_ON;
9618}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009619
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009620//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9621//it peeks whether it is &>, and then pushes back both chars.
9622//This function needs to save last *next_to_pgetc to buf[0]
9623//to make two pungetc() reliable. Currently,
9624// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009625static int
9626preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009627{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009628 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009629 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009630
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009631 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009632#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009633 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009634 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009635 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009636 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009637 int timeout = -1;
9638# if ENABLE_ASH_IDLE_TIMEOUT
9639 if (iflag) {
9640 const char *tmout_var = lookupvar("TMOUT");
9641 if (tmout_var) {
9642 timeout = atoi(tmout_var) * 1000;
9643 if (timeout <= 0)
9644 timeout = -1;
9645 }
9646 }
9647# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009648# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009649 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009650# endif
Denys Vlasenko20704f02011-03-23 17:59:27 +01009651 /* Unicode support should be activated even if LANG is set
9652 * _during_ shell execution, not only if it was set when
9653 * shell was started. Therefore, re-check LANG every time:
9654 */
9655 reinit_unicode(lookupvar("LANG"));
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009656 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009657 if (nr == 0) {
9658 /* Ctrl+C pressed */
9659 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009660 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009661 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009662 raise(SIGINT);
9663 return 1;
9664 }
Eric Andersenc470f442003-07-28 09:56:35 +00009665 goto retry;
9666 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +01009667 if (nr < 0) {
9668 if (errno == 0) {
9669 /* Ctrl+D pressed */
9670 nr = 0;
9671 }
9672# if ENABLE_ASH_IDLE_TIMEOUT
9673 else if (errno == EAGAIN && timeout > 0) {
9674 printf("\007timed out waiting for input: auto-logout\n");
9675 exitshell();
9676 }
9677# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009678 }
Eric Andersencb57d552001-06-28 07:25:16 +00009679 }
9680#else
Denys Vlasenko80542ba2011-05-08 21:23:43 +02009681 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009682#endif
9683
Denys Vlasenko80c5b682011-05-08 21:21:10 +02009684#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009685 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009686 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009687 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009688 if (flags >= 0 && (flags & O_NONBLOCK)) {
9689 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009690 if (fcntl(0, F_SETFL, flags) >= 0) {
9691 out2str("sh: turning off NDELAY mode\n");
9692 goto retry;
9693 }
9694 }
9695 }
9696 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009697#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009698 return nr;
9699}
9700
9701/*
9702 * Refill the input buffer and return the next input character:
9703 *
9704 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009705 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9706 * or we are reading from a string so we can't refill the buffer,
9707 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009708 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009709 * 4) Process input up to the next newline, deleting nul characters.
9710 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009711//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9712#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009713static int
Eric Andersenc470f442003-07-28 09:56:35 +00009714preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009715{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009716 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009717 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009718
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009719 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009720#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009721 if (g_parsefile->left_in_line == -1
9722 && g_parsefile->strpush->ap
9723 && g_parsefile->next_to_pgetc[-1] != ' '
9724 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009725 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009726 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009727 return PEOA;
9728 }
Eric Andersen2870d962001-07-02 17:27:21 +00009729#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009730 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009731 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009732 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9733 g_parsefile->left_in_line,
9734 g_parsefile->next_to_pgetc,
9735 g_parsefile->next_to_pgetc);
9736 if (--g_parsefile->left_in_line >= 0)
9737 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009738 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009739 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009740 * "pgetc" needs refilling.
9741 */
9742
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009743 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009744 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009745 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009746 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009747 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009748 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009749 /* even in failure keep left_in_line and next_to_pgetc
9750 * in lock step, for correct multi-layer pungetc.
9751 * left_in_line was decremented before preadbuffer(),
9752 * must inc next_to_pgetc: */
9753 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009754 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009755 }
Eric Andersencb57d552001-06-28 07:25:16 +00009756
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009757 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009758 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009759 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009760 again:
9761 more = preadfd();
9762 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009763 /* don't try reading again */
9764 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009765 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009766 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009767 return PEOF;
9768 }
9769 }
9770
Denis Vlasenko727752d2008-11-28 03:41:47 +00009771 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009772 * Set g_parsefile->left_in_line
9773 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009774 * NUL chars are deleted.
9775 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009776 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009777 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009778 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009779
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009780 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009781
Denis Vlasenko727752d2008-11-28 03:41:47 +00009782 c = *q;
9783 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009784 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009785 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009786 q++;
9787 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009788 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009789 break;
9790 }
Eric Andersencb57d552001-06-28 07:25:16 +00009791 }
9792
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009793 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009794 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9795 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009796 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009797 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009798 }
9799 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009800 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009801
Eric Andersencb57d552001-06-28 07:25:16 +00009802 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009803 char save = *q;
9804 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009805 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009806 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009807 }
9808
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009809 pgetc_debug("preadbuffer at %d:%p'%s'",
9810 g_parsefile->left_in_line,
9811 g_parsefile->next_to_pgetc,
9812 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009813 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009814}
9815
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009816#define pgetc_as_macro() \
9817 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009818 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009819 : preadbuffer() \
9820 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009821
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009822static int
9823pgetc(void)
9824{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009825 pgetc_debug("pgetc_fast at %d:%p'%s'",
9826 g_parsefile->left_in_line,
9827 g_parsefile->next_to_pgetc,
9828 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009829 return pgetc_as_macro();
9830}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009831
9832#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009833# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009834#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009835# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009836#endif
9837
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009838#if ENABLE_ASH_ALIAS
9839static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009840pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009841{
9842 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009843 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009844 pgetc_debug("pgetc_fast at %d:%p'%s'",
9845 g_parsefile->left_in_line,
9846 g_parsefile->next_to_pgetc,
9847 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009848 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009849 } while (c == PEOA);
9850 return c;
9851}
9852#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009853# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009854#endif
9855
9856/*
9857 * Read a line from the script.
9858 */
9859static char *
9860pfgets(char *line, int len)
9861{
9862 char *p = line;
9863 int nleft = len;
9864 int c;
9865
9866 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009867 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009868 if (c == PEOF) {
9869 if (p == line)
9870 return NULL;
9871 break;
9872 }
9873 *p++ = c;
9874 if (c == '\n')
9875 break;
9876 }
9877 *p = '\0';
9878 return line;
9879}
9880
Eric Andersenc470f442003-07-28 09:56:35 +00009881/*
9882 * Undo the last call to pgetc. Only one character may be pushed back.
9883 * PEOF may be pushed back.
9884 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009885static void
Eric Andersenc470f442003-07-28 09:56:35 +00009886pungetc(void)
9887{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009888 g_parsefile->left_in_line++;
9889 g_parsefile->next_to_pgetc--;
9890 pgetc_debug("pushed back to %d:%p'%s'",
9891 g_parsefile->left_in_line,
9892 g_parsefile->next_to_pgetc,
9893 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009894}
9895
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009896/*
9897 * To handle the "." command, a stack of input files is used. Pushfile
9898 * adds a new entry to the stack and popfile restores the previous level.
9899 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009900static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009901pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009902{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009903 struct parsefile *pf;
9904
Denis Vlasenko597906c2008-02-20 16:38:54 +00009905 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009906 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009907 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009908 /*pf->strpush = NULL; - ckzalloc did it */
9909 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009910 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009911}
9912
9913static void
9914popfile(void)
9915{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009916 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009917
Denis Vlasenkob012b102007-02-19 22:43:01 +00009918 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009919 if (pf->pf_fd >= 0)
9920 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009921 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009922 while (pf->strpush)
9923 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009924 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009925 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009926 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009927}
9928
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009929/*
9930 * Return to top level.
9931 */
9932static void
9933popallfiles(void)
9934{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009935 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009936 popfile();
9937}
9938
9939/*
9940 * Close the file(s) that the shell is reading commands from. Called
9941 * after a fork is done.
9942 */
9943static void
9944closescript(void)
9945{
9946 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009947 if (g_parsefile->pf_fd > 0) {
9948 close(g_parsefile->pf_fd);
9949 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009950 }
9951}
9952
9953/*
9954 * Like setinputfile, but takes an open file descriptor. Call this with
9955 * interrupts off.
9956 */
9957static void
9958setinputfd(int fd, int push)
9959{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009960 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009961 if (push) {
9962 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009963 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009964 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009965 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009966 if (g_parsefile->buf == NULL)
9967 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009968 g_parsefile->left_in_buffer = 0;
9969 g_parsefile->left_in_line = 0;
9970 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009971}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009972
Eric Andersenc470f442003-07-28 09:56:35 +00009973/*
9974 * Set the input to take input from a file. If push is set, push the
9975 * old input onto the stack first.
9976 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009977static int
9978setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009979{
9980 int fd;
9981 int fd2;
9982
Denis Vlasenkob012b102007-02-19 22:43:01 +00009983 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009984 fd = open(fname, O_RDONLY);
9985 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009986 if (flags & INPUT_NOFILE_OK)
9987 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009988 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009989 }
Eric Andersenc470f442003-07-28 09:56:35 +00009990 if (fd < 10) {
9991 fd2 = copyfd(fd, 10);
9992 close(fd);
9993 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009994 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009995 fd = fd2;
9996 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009997 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009998 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009999 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010000 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010001}
10002
Eric Andersencb57d552001-06-28 07:25:16 +000010003/*
10004 * Like setinputfile, but takes input from a string.
10005 */
Eric Andersenc470f442003-07-28 09:56:35 +000010006static void
10007setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000010008{
Denis Vlasenkob012b102007-02-19 22:43:01 +000010009 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010010 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010011 g_parsefile->next_to_pgetc = string;
10012 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010013 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010014 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010015 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010016}
10017
10018
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010019/* ============ mail.c
10020 *
10021 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000010022 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010023
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010024#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000010025
Eric Andersencb57d552001-06-28 07:25:16 +000010026#define MAXMBOXES 10
10027
Eric Andersenc470f442003-07-28 09:56:35 +000010028/* times of mailboxes */
10029static time_t mailtime[MAXMBOXES];
10030/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010031static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000010032
Eric Andersencb57d552001-06-28 07:25:16 +000010033/*
Eric Andersenc470f442003-07-28 09:56:35 +000010034 * Print appropriate message(s) if mail has arrived.
10035 * If mail_var_path_changed is set,
10036 * then the value of MAIL has mail_var_path_changed,
10037 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000010038 */
Eric Andersenc470f442003-07-28 09:56:35 +000010039static void
10040chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010041{
Eric Andersencb57d552001-06-28 07:25:16 +000010042 const char *mpath;
10043 char *p;
10044 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +000010045 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +000010046 struct stackmark smark;
10047 struct stat statb;
10048
Eric Andersencb57d552001-06-28 07:25:16 +000010049 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000010050 mpath = mpathset() ? mpathval() : mailval();
10051 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020010052 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +000010053 if (p == NULL)
10054 break;
10055 if (*p == '\0')
10056 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010057 for (q = p; *q; q++)
10058 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000010059#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000010060 if (q[-1] != '/')
10061 abort();
10062#endif
Eric Andersenc470f442003-07-28 09:56:35 +000010063 q[-1] = '\0'; /* delete trailing '/' */
10064 if (stat(p, &statb) < 0) {
10065 *mtp = 0;
10066 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000010067 }
Eric Andersenc470f442003-07-28 09:56:35 +000010068 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
10069 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +020010070 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +000010071 pathopt ? pathopt : "you have mail"
10072 );
10073 }
10074 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +000010075 }
Eric Andersenc470f442003-07-28 09:56:35 +000010076 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010077 popstackmark(&smark);
10078}
Eric Andersencb57d552001-06-28 07:25:16 +000010079
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010080static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010081changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000010082{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010083 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010084}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010085
Denis Vlasenko131ae172007-02-18 13:00:19 +000010086#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000010087
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010088
10089/* ============ ??? */
10090
Eric Andersencb57d552001-06-28 07:25:16 +000010091/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010092 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010093 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010094static void
10095setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010096{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010097 char **newparam;
10098 char **ap;
10099 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010100
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010101 for (nparam = 0; argv[nparam]; nparam++)
10102 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010103 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10104 while (*argv) {
10105 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010106 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010107 *ap = NULL;
10108 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010109 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010110 shellparam.nparam = nparam;
10111 shellparam.p = newparam;
10112#if ENABLE_ASH_GETOPTS
10113 shellparam.optind = 1;
10114 shellparam.optoff = -1;
10115#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010116}
10117
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010118/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010119 * Process shell options. The global variable argptr contains a pointer
10120 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010121 *
10122 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10123 * For a non-interactive shell, an error condition encountered
10124 * by a special built-in ... shall cause the shell to write a diagnostic message
10125 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010126 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010127 * ...
10128 * Utility syntax error (option or operand error) Shall exit
10129 * ...
10130 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10131 * we see that bash does not do that (set "finishes" with error code 1 instead,
10132 * and shell continues), and people rely on this behavior!
10133 * Testcase:
10134 * set -o barfoo 2>/dev/null
10135 * echo $?
10136 *
10137 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010138 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010139static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010140plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010141{
10142 int i;
10143
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010144 if (name) {
10145 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010146 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010147 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010148 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010149 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010150 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010151 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010152 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010153 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010154 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010155 if (val) {
10156 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10157 } else {
10158 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10159 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010160 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010161 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010162}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010163static void
10164setoption(int flag, int val)
10165{
10166 int i;
10167
10168 for (i = 0; i < NOPTS; i++) {
10169 if (optletters(i) == flag) {
10170 optlist[i] = val;
10171 return;
10172 }
10173 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010174 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010175 /* NOTREACHED */
10176}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010177static int
Eric Andersenc470f442003-07-28 09:56:35 +000010178options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010179{
10180 char *p;
10181 int val;
10182 int c;
10183
10184 if (cmdline)
10185 minusc = NULL;
10186 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010187 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010188 if (c != '-' && c != '+')
10189 break;
10190 argptr++;
10191 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010192 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010193 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010194 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010195 if (!cmdline) {
10196 /* "-" means turn off -x and -v */
10197 if (p[0] == '\0')
10198 xflag = vflag = 0;
10199 /* "--" means reset params */
10200 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010201 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010202 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010203 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010204 }
Eric Andersencb57d552001-06-28 07:25:16 +000010205 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010206 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010207 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010208 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010209 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010210 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010211 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010212 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010213 /* it already printed err message */
10214 return 1; /* error */
10215 }
Eric Andersencb57d552001-06-28 07:25:16 +000010216 if (*argptr)
10217 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010218 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10219 isloginsh = 1;
10220 /* bash does not accept +-login, we also won't */
10221 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010222 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010223 isloginsh = 1;
10224 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010225 } else {
10226 setoption(c, val);
10227 }
10228 }
10229 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010230 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010231}
10232
Eric Andersencb57d552001-06-28 07:25:16 +000010233/*
Eric Andersencb57d552001-06-28 07:25:16 +000010234 * The shift builtin command.
10235 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010236static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010237shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010238{
10239 int n;
10240 char **ap1, **ap2;
10241
10242 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010243 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010244 n = number(argv[1]);
10245 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010246 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010247 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010248 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010249 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010250 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010251 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010252 }
10253 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010254 while ((*ap2++ = *ap1++) != NULL)
10255 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010256#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010257 shellparam.optind = 1;
10258 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010259#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010260 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010261 return 0;
10262}
10263
Eric Andersencb57d552001-06-28 07:25:16 +000010264/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010265 * POSIX requires that 'set' (but not export or readonly) output the
10266 * variables in lexicographic order - by the locale's collating order (sigh).
10267 * Maybe we could keep them in an ordered balanced binary tree
10268 * instead of hashed lists.
10269 * For now just roll 'em through qsort for printing...
10270 */
10271static int
10272showvars(const char *sep_prefix, int on, int off)
10273{
10274 const char *sep;
10275 char **ep, **epend;
10276
10277 ep = listvars(on, off, &epend);
10278 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10279
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010280 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010281
10282 for (; ep < epend; ep++) {
10283 const char *p;
10284 const char *q;
10285
10286 p = strchrnul(*ep, '=');
10287 q = nullstr;
10288 if (*p)
10289 q = single_quote(++p);
10290 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10291 }
10292 return 0;
10293}
10294
10295/*
Eric Andersencb57d552001-06-28 07:25:16 +000010296 * The set command builtin.
10297 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010298static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010299setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010300{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010301 int retval;
10302
Denis Vlasenko68404f12008-03-17 09:00:54 +000010303 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010304 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010305
Denis Vlasenkob012b102007-02-19 22:43:01 +000010306 INT_OFF;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010010307 retval = options(/*cmdline:*/ 0);
10308 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010309 optschanged();
10310 if (*argptr != NULL) {
10311 setparam(argptr);
10312 }
Eric Andersencb57d552001-06-28 07:25:16 +000010313 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010314 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010315 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010316}
10317
Denis Vlasenko131ae172007-02-18 13:00:19 +000010318#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010319static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010320change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010321{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010322 uint32_t t;
10323
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010324 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010325 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010326 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010327 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010328 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010329 vrandom.flags &= ~VNOFUNC;
10330 } else {
10331 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010332 t = strtoul(value, NULL, 10);
10333 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010334 }
Eric Andersenef02f822004-03-11 13:34:24 +000010335}
Eric Andersen16767e22004-03-16 05:14:10 +000010336#endif
10337
Denis Vlasenko131ae172007-02-18 13:00:19 +000010338#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010339static int
Eric Andersenc470f442003-07-28 09:56:35 +000010340getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010341{
10342 char *p, *q;
10343 char c = '?';
10344 int done = 0;
10345 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010346 char s[12];
10347 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010348
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010349 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010350 return 1;
10351 optnext = optfirst + *param_optind - 1;
10352
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010353 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010354 p = NULL;
10355 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010356 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010357 if (p == NULL || *p == '\0') {
10358 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010359 p = *optnext;
10360 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010361 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010362 p = NULL;
10363 done = 1;
10364 goto out;
10365 }
10366 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010367 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010368 goto atend;
10369 }
10370
10371 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010372 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010373 if (*q == '\0') {
10374 if (optstr[0] == ':') {
10375 s[0] = c;
10376 s[1] = '\0';
10377 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010378 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010379 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010380 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010381 }
10382 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010383 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010384 }
10385 if (*++q == ':')
10386 q++;
10387 }
10388
10389 if (*++q == ':') {
10390 if (*p == '\0' && (p = *optnext) == NULL) {
10391 if (optstr[0] == ':') {
10392 s[0] = c;
10393 s[1] = '\0';
10394 err |= setvarsafe("OPTARG", s, 0);
10395 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010396 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010397 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010398 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010399 c = '?';
10400 }
Eric Andersenc470f442003-07-28 09:56:35 +000010401 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010402 }
10403
10404 if (p == *optnext)
10405 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010406 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010407 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010408 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010409 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010410 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010411 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010412 *param_optind = optnext - optfirst + 1;
10413 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010414 err |= setvarsafe("OPTIND", s, VNOFUNC);
10415 s[0] = c;
10416 s[1] = '\0';
10417 err |= setvarsafe(optvar, s, 0);
10418 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010419 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010420 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010421 flush_stdout_stderr();
10422 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010423 }
10424 return done;
10425}
Eric Andersenc470f442003-07-28 09:56:35 +000010426
10427/*
10428 * The getopts builtin. Shellparam.optnext points to the next argument
10429 * to be processed. Shellparam.optptr points to the next character to
10430 * be processed in the current argument. If shellparam.optnext is NULL,
10431 * then it's the first time getopts has been called.
10432 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010433static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010434getoptscmd(int argc, char **argv)
10435{
10436 char **optbase;
10437
10438 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010439 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010440 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010441 optbase = shellparam.p;
10442 if (shellparam.optind > shellparam.nparam + 1) {
10443 shellparam.optind = 1;
10444 shellparam.optoff = -1;
10445 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010446 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010447 optbase = &argv[3];
10448 if (shellparam.optind > argc - 2) {
10449 shellparam.optind = 1;
10450 shellparam.optoff = -1;
10451 }
10452 }
10453
10454 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010455 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010456}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010457#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010458
Eric Andersencb57d552001-06-28 07:25:16 +000010459
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010460/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010461
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010462struct heredoc {
10463 struct heredoc *next; /* next here document in list */
10464 union node *here; /* redirection node */
10465 char *eofmark; /* string indicating end of input */
10466 smallint striptabs; /* if set, strip leading tabs */
10467};
10468
10469static smallint tokpushback; /* last token pushed back */
10470static smallint parsebackquote; /* nonzero if we are inside backquotes */
10471static smallint quoteflag; /* set if (part of) last token was quoted */
10472static token_id_t lasttoken; /* last token read (integer id Txxx) */
10473static struct heredoc *heredoclist; /* list of here documents to read */
10474static char *wordtext; /* text of last word returned by readtoken */
10475static struct nodelist *backquotelist;
10476static union node *redirnode;
10477static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010478
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010479static const char *
10480tokname(char *buf, int tok)
10481{
10482 if (tok < TSEMI)
10483 return tokname_array[tok] + 1;
10484 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10485 return buf;
10486}
10487
10488/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010489 * Called when an unexpected token is read during the parse. The argument
10490 * is the token that is expected, or -1 if more than one type of token can
10491 * occur at this point.
10492 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010493static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010494static void
10495raise_error_unexpected_syntax(int token)
10496{
10497 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010498 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010499 int l;
10500
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010501 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010502 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010503 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010504 raise_error_syntax(msg);
10505 /* NOTREACHED */
10506}
Eric Andersencb57d552001-06-28 07:25:16 +000010507
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010508#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010509
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010510/* parsing is heavily cross-recursive, need these forward decls */
10511static union node *andor(void);
10512static union node *pipeline(void);
10513static union node *parse_command(void);
10514static void parseheredoc(void);
10515static char peektoken(void);
10516static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010517
Eric Andersenc470f442003-07-28 09:56:35 +000010518static union node *
10519list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010520{
10521 union node *n1, *n2, *n3;
10522 int tok;
10523
Eric Andersenc470f442003-07-28 09:56:35 +000010524 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10525 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010526 return NULL;
10527 n1 = NULL;
10528 for (;;) {
10529 n2 = andor();
10530 tok = readtoken();
10531 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010532 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010533 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010534 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010535 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010536 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010537 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010538 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010539 n2 = n3;
10540 }
10541 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010542 }
10543 }
10544 if (n1 == NULL) {
10545 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010546 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010547 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010548 n3->type = NSEMI;
10549 n3->nbinary.ch1 = n1;
10550 n3->nbinary.ch2 = n2;
10551 n1 = n3;
10552 }
10553 switch (tok) {
10554 case TBACKGND:
10555 case TSEMI:
10556 tok = readtoken();
10557 /* fall through */
10558 case TNL:
10559 if (tok == TNL) {
10560 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010561 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010562 return n1;
10563 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010564 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010565 }
Eric Andersenc470f442003-07-28 09:56:35 +000010566 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010567 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010568 return n1;
10569 break;
10570 case TEOF:
10571 if (heredoclist)
10572 parseheredoc();
10573 else
Eric Andersenc470f442003-07-28 09:56:35 +000010574 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010575 return n1;
10576 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010577 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010578 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010579 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010580 return n1;
10581 }
10582 }
10583}
10584
Eric Andersenc470f442003-07-28 09:56:35 +000010585static union node *
10586andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010587{
Eric Andersencb57d552001-06-28 07:25:16 +000010588 union node *n1, *n2, *n3;
10589 int t;
10590
Eric Andersencb57d552001-06-28 07:25:16 +000010591 n1 = pipeline();
10592 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010593 t = readtoken();
10594 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010595 t = NAND;
10596 } else if (t == TOR) {
10597 t = NOR;
10598 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010599 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010600 return n1;
10601 }
Eric Andersenc470f442003-07-28 09:56:35 +000010602 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010603 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010604 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010605 n3->type = t;
10606 n3->nbinary.ch1 = n1;
10607 n3->nbinary.ch2 = n2;
10608 n1 = n3;
10609 }
10610}
10611
Eric Andersenc470f442003-07-28 09:56:35 +000010612static union node *
10613pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010614{
Eric Andersencb57d552001-06-28 07:25:16 +000010615 union node *n1, *n2, *pipenode;
10616 struct nodelist *lp, *prev;
10617 int negate;
10618
10619 negate = 0;
10620 TRACE(("pipeline: entered\n"));
10621 if (readtoken() == TNOT) {
10622 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010623 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010624 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010625 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010626 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010627 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010628 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010629 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010630 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010631 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010632 pipenode->npipe.cmdlist = lp;
10633 lp->n = n1;
10634 do {
10635 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010636 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010637 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010638 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010639 prev->next = lp;
10640 } while (readtoken() == TPIPE);
10641 lp->next = NULL;
10642 n1 = pipenode;
10643 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010644 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010645 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010646 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010647 n2->type = NNOT;
10648 n2->nnot.com = n1;
10649 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010650 }
10651 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010652}
10653
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010654static union node *
10655makename(void)
10656{
10657 union node *n;
10658
Denis Vlasenko597906c2008-02-20 16:38:54 +000010659 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010660 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010661 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010662 n->narg.text = wordtext;
10663 n->narg.backquote = backquotelist;
10664 return n;
10665}
10666
10667static void
10668fixredir(union node *n, const char *text, int err)
10669{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010670 int fd;
10671
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010672 TRACE(("Fix redir %s %d\n", text, err));
10673 if (!err)
10674 n->ndup.vname = NULL;
10675
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010676 fd = bb_strtou(text, NULL, 10);
10677 if (!errno && fd >= 0)
10678 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010679 else if (LONE_DASH(text))
10680 n->ndup.dupfd = -1;
10681 else {
10682 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010683 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010684 n->ndup.vname = makename();
10685 }
10686}
10687
10688/*
10689 * Returns true if the text contains nothing to expand (no dollar signs
10690 * or backquotes).
10691 */
10692static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010693noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010694{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010695 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010696
Denys Vlasenkocd716832009-11-28 22:14:02 +010010697 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010698 if (c == CTLQUOTEMARK)
10699 continue;
10700 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010701 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010702 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010703 return 0;
10704 }
10705 return 1;
10706}
10707
10708static void
10709parsefname(void)
10710{
10711 union node *n = redirnode;
10712
10713 if (readtoken() != TWORD)
10714 raise_error_unexpected_syntax(-1);
10715 if (n->type == NHERE) {
10716 struct heredoc *here = heredoc;
10717 struct heredoc *p;
10718 int i;
10719
10720 if (quoteflag == 0)
10721 n->type = NXHERE;
10722 TRACE(("Here document %d\n", n->type));
10723 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010724 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010725 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010726 here->eofmark = wordtext;
10727 here->next = NULL;
10728 if (heredoclist == NULL)
10729 heredoclist = here;
10730 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010731 for (p = heredoclist; p->next; p = p->next)
10732 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733 p->next = here;
10734 }
10735 } else if (n->type == NTOFD || n->type == NFROMFD) {
10736 fixredir(n, wordtext, 0);
10737 } else {
10738 n->nfile.fname = makename();
10739 }
10740}
Eric Andersencb57d552001-06-28 07:25:16 +000010741
Eric Andersenc470f442003-07-28 09:56:35 +000010742static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010743simplecmd(void)
10744{
10745 union node *args, **app;
10746 union node *n = NULL;
10747 union node *vars, **vpp;
10748 union node **rpp, *redir;
10749 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010750#if ENABLE_ASH_BASH_COMPAT
10751 smallint double_brackets_flag = 0;
10752#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010753
10754 args = NULL;
10755 app = &args;
10756 vars = NULL;
10757 vpp = &vars;
10758 redir = NULL;
10759 rpp = &redir;
10760
10761 savecheckkwd = CHKALIAS;
10762 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010763 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010764 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010765 t = readtoken();
10766 switch (t) {
10767#if ENABLE_ASH_BASH_COMPAT
10768 case TAND: /* "&&" */
10769 case TOR: /* "||" */
10770 if (!double_brackets_flag) {
10771 tokpushback = 1;
10772 goto out;
10773 }
10774 wordtext = (char *) (t == TAND ? "-a" : "-o");
10775#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010776 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010777 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010778 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010779 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010780 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010781#if ENABLE_ASH_BASH_COMPAT
10782 if (strcmp("[[", wordtext) == 0)
10783 double_brackets_flag = 1;
10784 else if (strcmp("]]", wordtext) == 0)
10785 double_brackets_flag = 0;
10786#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010787 n->narg.backquote = backquotelist;
10788 if (savecheckkwd && isassignment(wordtext)) {
10789 *vpp = n;
10790 vpp = &n->narg.next;
10791 } else {
10792 *app = n;
10793 app = &n->narg.next;
10794 savecheckkwd = 0;
10795 }
10796 break;
10797 case TREDIR:
10798 *rpp = n = redirnode;
10799 rpp = &n->nfile.next;
10800 parsefname(); /* read name of redirection file */
10801 break;
10802 case TLP:
10803 if (args && app == &args->narg.next
10804 && !vars && !redir
10805 ) {
10806 struct builtincmd *bcmd;
10807 const char *name;
10808
10809 /* We have a function */
10810 if (readtoken() != TRP)
10811 raise_error_unexpected_syntax(TRP);
10812 name = n->narg.text;
10813 if (!goodname(name)
10814 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10815 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010816 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010817 }
10818 n->type = NDEFUN;
10819 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10820 n->narg.next = parse_command();
10821 return n;
10822 }
10823 /* fall through */
10824 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010825 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010826 goto out;
10827 }
10828 }
10829 out:
10830 *app = NULL;
10831 *vpp = NULL;
10832 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010833 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010834 n->type = NCMD;
10835 n->ncmd.args = args;
10836 n->ncmd.assign = vars;
10837 n->ncmd.redirect = redir;
10838 return n;
10839}
10840
10841static union node *
10842parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010843{
Eric Andersencb57d552001-06-28 07:25:16 +000010844 union node *n1, *n2;
10845 union node *ap, **app;
10846 union node *cp, **cpp;
10847 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010848 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010849 int t;
10850
10851 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010852 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010853
Eric Andersencb57d552001-06-28 07:25:16 +000010854 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010855 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010856 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010857 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010858 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010859 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010860 n1->type = NIF;
10861 n1->nif.test = list(0);
10862 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010863 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010864 n1->nif.ifpart = list(0);
10865 n2 = n1;
10866 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010867 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010868 n2 = n2->nif.elsepart;
10869 n2->type = NIF;
10870 n2->nif.test = list(0);
10871 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010872 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010873 n2->nif.ifpart = list(0);
10874 }
10875 if (lasttoken == TELSE)
10876 n2->nif.elsepart = list(0);
10877 else {
10878 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010879 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010880 }
Eric Andersenc470f442003-07-28 09:56:35 +000010881 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010882 break;
10883 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010884 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010885 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010886 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010887 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010888 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010889 got = readtoken();
10890 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010891 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010892 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010893 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010894 }
10895 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010896 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010897 break;
10898 }
10899 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010900 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010901 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010902 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010903 n1->type = NFOR;
10904 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010905 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010906 if (readtoken() == TIN) {
10907 app = &ap;
10908 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010909 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010910 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010911 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010912 n2->narg.text = wordtext;
10913 n2->narg.backquote = backquotelist;
10914 *app = n2;
10915 app = &n2->narg.next;
10916 }
10917 *app = NULL;
10918 n1->nfor.args = ap;
10919 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010920 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010921 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010922 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010923 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010924 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010925 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010926 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010927 n1->nfor.args = n2;
10928 /*
10929 * Newline or semicolon here is optional (but note
10930 * that the original Bourne shell only allowed NL).
10931 */
10932 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010933 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010934 }
Eric Andersenc470f442003-07-28 09:56:35 +000010935 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010936 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010937 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010938 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010939 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010940 break;
10941 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010942 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010943 n1->type = NCASE;
10944 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010945 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010946 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010947 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010948 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010949 n2->narg.text = wordtext;
10950 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010951 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010952 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010953 } while (readtoken() == TNL);
10954 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010955 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010956 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010957 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010958 checkkwd = CHKNL | CHKKWD;
10959 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010960 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010961 if (lasttoken == TLP)
10962 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010963 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010964 cp->type = NCLIST;
10965 app = &cp->nclist.pattern;
10966 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010967 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010968 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010969 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010970 ap->narg.text = wordtext;
10971 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010972 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010973 break;
10974 app = &ap->narg.next;
10975 readtoken();
10976 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010977 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010978 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010979 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010980 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010981
Eric Andersenc470f442003-07-28 09:56:35 +000010982 cpp = &cp->nclist.next;
10983
10984 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010985 t = readtoken();
10986 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010987 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010988 raise_error_unexpected_syntax(TENDCASE);
10989 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010990 }
Eric Andersenc470f442003-07-28 09:56:35 +000010991 }
Eric Andersencb57d552001-06-28 07:25:16 +000010992 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010993 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010994 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010995 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010996 n1->type = NSUBSHELL;
10997 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010998 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010999 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000011000 break;
11001 case TBEGIN:
11002 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011003 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000011004 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011005 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000011006 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011007 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011008 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000011009 }
11010
Eric Andersenc470f442003-07-28 09:56:35 +000011011 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011012 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000011013
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011014 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000011015 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000011016 checkkwd = CHKKWD | CHKALIAS;
11017 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011018 while (readtoken() == TREDIR) {
11019 *rpp = n2 = redirnode;
11020 rpp = &n2->nfile.next;
11021 parsefname();
11022 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011023 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011024 *rpp = NULL;
11025 if (redir) {
11026 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011027 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000011028 n2->type = NREDIR;
11029 n2->nredir.n = n1;
11030 n1 = n2;
11031 }
11032 n1->nredir.redirect = redir;
11033 }
Eric Andersencb57d552001-06-28 07:25:16 +000011034 return n1;
11035}
11036
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011037#if ENABLE_ASH_BASH_COMPAT
11038static int decode_dollar_squote(void)
11039{
11040 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
11041 int c, cnt;
11042 char *p;
11043 char buf[4];
11044
11045 c = pgetc();
11046 p = strchr(C_escapes, c);
11047 if (p) {
11048 buf[0] = c;
11049 p = buf;
11050 cnt = 3;
11051 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
11052 do {
11053 c = pgetc();
11054 *++p = c;
11055 } while ((unsigned char)(c - '0') <= 7 && --cnt);
11056 pungetc();
11057 } else if (c == 'x') { /* \xHH */
11058 do {
11059 c = pgetc();
11060 *++p = c;
11061 } while (isxdigit(c) && --cnt);
11062 pungetc();
11063 if (cnt == 3) { /* \x but next char is "bad" */
11064 c = 'x';
11065 goto unrecognized;
11066 }
11067 } else { /* simple seq like \\ or \t */
11068 p++;
11069 }
11070 *p = '\0';
11071 p = buf;
11072 c = bb_process_escape_sequence((void*)&p);
11073 } else { /* unrecognized "\z": print both chars unless ' or " */
11074 if (c != '\'' && c != '"') {
11075 unrecognized:
11076 c |= 0x100; /* "please encode \, then me" */
11077 }
11078 }
11079 return c;
11080}
11081#endif
11082
Eric Andersencb57d552001-06-28 07:25:16 +000011083/*
11084 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
11085 * is not NULL, read a here document. In the latter case, eofmark is the
11086 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010011087 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000011088 * is the first character of the input token or document.
11089 *
11090 * Because C does not have internal subroutines, I have simulated them
11091 * using goto's to implement the subroutine linkage. The following macros
11092 * will run code that appears at the end of readtoken1.
11093 */
Eric Andersen2870d962001-07-02 17:27:21 +000011094#define CHECKEND() {goto checkend; checkend_return:;}
11095#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11096#define PARSESUB() {goto parsesub; parsesub_return:;}
11097#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11098#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11099#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011100static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011101readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011102{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011103 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011104 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011105 char *out;
11106 int len;
11107 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011108 struct nodelist *bqlist;
11109 smallint quotef;
11110 smallint dblquote;
11111 smallint oldstyle;
11112 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011113#if ENABLE_ASH_EXPAND_PRMT
11114 smallint pssyntax; /* we are expanding a prompt string */
11115#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011116 int varnest; /* levels of variables expansion */
11117 int arinest; /* levels of arithmetic expansion */
11118 int parenlevel; /* levels of parens in arithmetic */
11119 int dqvarnest; /* levels of variables expansion within double quotes */
11120
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011121 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011122
Eric Andersencb57d552001-06-28 07:25:16 +000011123#if __GNUC__
11124 /* Avoid longjmp clobbering */
11125 (void) &out;
11126 (void) &quotef;
11127 (void) &dblquote;
11128 (void) &varnest;
11129 (void) &arinest;
11130 (void) &parenlevel;
11131 (void) &dqvarnest;
11132 (void) &oldstyle;
11133 (void) &prevsyntax;
11134 (void) &syntax;
11135#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011136 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011137 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011138 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011139 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011140#if ENABLE_ASH_EXPAND_PRMT
11141 pssyntax = (syntax == PSSYNTAX);
11142 if (pssyntax)
11143 syntax = DQSYNTAX;
11144#endif
11145 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011146 varnest = 0;
11147 arinest = 0;
11148 parenlevel = 0;
11149 dqvarnest = 0;
11150
11151 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011152 loop:
11153 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011154 CHECKEND(); /* set c to PEOF if at end of here document */
11155 for (;;) { /* until end of line or end of word */
11156 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11157 switch (SIT(c, syntax)) {
11158 case CNL: /* '\n' */
11159 if (syntax == BASESYNTAX)
11160 goto endword; /* exit outer loop */
11161 USTPUTC(c, out);
11162 g_parsefile->linno++;
11163 setprompt_if(doprompt, 2);
11164 c = pgetc();
11165 goto loop; /* continue outer loop */
11166 case CWORD:
11167 USTPUTC(c, out);
11168 break;
11169 case CCTL:
11170 if (eofmark == NULL || dblquote)
11171 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011172#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011173 if (c == '\\' && bash_dollar_squote) {
11174 c = decode_dollar_squote();
11175 if (c & 0x100) {
11176 USTPUTC('\\', out);
11177 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011178 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011179 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011180#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011181 USTPUTC(c, out);
11182 break;
11183 case CBACK: /* backslash */
11184 c = pgetc_without_PEOA();
11185 if (c == PEOF) {
11186 USTPUTC(CTLESC, out);
11187 USTPUTC('\\', out);
11188 pungetc();
11189 } else if (c == '\n') {
11190 setprompt_if(doprompt, 2);
11191 } else {
11192#if ENABLE_ASH_EXPAND_PRMT
11193 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011194 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011195 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011196 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011197#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011198 /* Backslash is retained if we are in "str" and next char isn't special */
11199 if (dblquote
11200 && c != '\\'
11201 && c != '`'
11202 && c != '$'
11203 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011204 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011205 USTPUTC(CTLESC, out);
11206 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011207 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011208 if (SIT(c, SQSYNTAX) == CCTL)
11209 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011210 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011211 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011212 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011213 break;
11214 case CSQUOTE:
11215 syntax = SQSYNTAX;
11216 quotemark:
11217 if (eofmark == NULL) {
11218 USTPUTC(CTLQUOTEMARK, out);
11219 }
11220 break;
11221 case CDQUOTE:
11222 syntax = DQSYNTAX;
11223 dblquote = 1;
11224 goto quotemark;
11225 case CENDQUOTE:
11226 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11227 if (eofmark != NULL && arinest == 0
11228 && varnest == 0
11229 ) {
11230 USTPUTC(c, out);
11231 } else {
11232 if (dqvarnest == 0) {
11233 syntax = BASESYNTAX;
11234 dblquote = 0;
11235 }
11236 quotef = 1;
11237 goto quotemark;
11238 }
11239 break;
11240 case CVAR: /* '$' */
11241 PARSESUB(); /* parse substitution */
11242 break;
11243 case CENDVAR: /* '}' */
11244 if (varnest > 0) {
11245 varnest--;
11246 if (dqvarnest > 0) {
11247 dqvarnest--;
11248 }
11249 c = CTLENDVAR;
11250 }
11251 USTPUTC(c, out);
11252 break;
11253#if ENABLE_SH_MATH_SUPPORT
11254 case CLP: /* '(' in arithmetic */
11255 parenlevel++;
11256 USTPUTC(c, out);
11257 break;
11258 case CRP: /* ')' in arithmetic */
11259 if (parenlevel > 0) {
11260 parenlevel--;
11261 } else {
11262 if (pgetc() == ')') {
11263 if (--arinest == 0) {
11264 syntax = prevsyntax;
11265 dblquote = (syntax == DQSYNTAX);
11266 c = CTLENDARI;
11267 }
11268 } else {
11269 /*
11270 * unbalanced parens
11271 * (don't 2nd guess - no error)
11272 */
11273 pungetc();
11274 }
11275 }
11276 USTPUTC(c, out);
11277 break;
11278#endif
11279 case CBQUOTE: /* '`' */
11280 PARSEBACKQOLD();
11281 break;
11282 case CENDFILE:
11283 goto endword; /* exit outer loop */
11284 case CIGN:
11285 break;
11286 default:
11287 if (varnest == 0) {
11288#if ENABLE_ASH_BASH_COMPAT
11289 if (c == '&') {
11290 if (pgetc() == '>')
11291 c = 0x100 + '>'; /* flag &> */
11292 pungetc();
11293 }
11294#endif
11295 goto endword; /* exit outer loop */
11296 }
11297 IF_ASH_ALIAS(if (c != PEOA))
11298 USTPUTC(c, out);
11299 }
11300 c = pgetc_fast();
11301 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011302 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011303
Mike Frysinger98c52642009-04-02 10:02:37 +000011304#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011305 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011306 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011307#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011308 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011309 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011310 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011311 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011312 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011313 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011314 }
11315 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011316 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011317 out = stackblock();
11318 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011319 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011320 && quotef == 0
11321 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011322 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011323 PARSEREDIR(); /* passed as params: out, c */
11324 lasttoken = TREDIR;
11325 return lasttoken;
11326 }
11327 /* else: non-number X seen, interpret it
11328 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011329 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011330 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011331 }
11332 quoteflag = quotef;
11333 backquotelist = bqlist;
11334 grabstackblock(len);
11335 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011336 lasttoken = TWORD;
11337 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011338/* end of readtoken routine */
11339
Eric Andersencb57d552001-06-28 07:25:16 +000011340/*
11341 * Check to see whether we are at the end of the here document. When this
11342 * is called, c is set to the first character of the next input line. If
11343 * we are at the end of the here document, this routine sets the c to PEOF.
11344 */
Eric Andersenc470f442003-07-28 09:56:35 +000011345checkend: {
11346 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011347#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011348 if (c == PEOA)
11349 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011350#endif
11351 if (striptabs) {
11352 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011353 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011354 }
Eric Andersenc470f442003-07-28 09:56:35 +000011355 }
11356 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011357 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011358 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011359
Eric Andersenc470f442003-07-28 09:56:35 +000011360 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011361 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11362 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011363 if (*p == '\n' && *q == '\0') {
11364 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011365 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011366 needprompt = doprompt;
11367 } else {
11368 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011369 }
11370 }
11371 }
11372 }
Eric Andersenc470f442003-07-28 09:56:35 +000011373 goto checkend_return;
11374}
Eric Andersencb57d552001-06-28 07:25:16 +000011375
Eric Andersencb57d552001-06-28 07:25:16 +000011376/*
11377 * Parse a redirection operator. The variable "out" points to a string
11378 * specifying the fd to be redirected. The variable "c" contains the
11379 * first character of the redirection operator.
11380 */
Eric Andersenc470f442003-07-28 09:56:35 +000011381parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011382 /* out is already checked to be a valid number or "" */
11383 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011384 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011385
Denis Vlasenko597906c2008-02-20 16:38:54 +000011386 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011387 if (c == '>') {
11388 np->nfile.fd = 1;
11389 c = pgetc();
11390 if (c == '>')
11391 np->type = NAPPEND;
11392 else if (c == '|')
11393 np->type = NCLOBBER;
11394 else if (c == '&')
11395 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011396 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 else {
11398 np->type = NTO;
11399 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011400 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011401 }
11402#if ENABLE_ASH_BASH_COMPAT
11403 else if (c == 0x100 + '>') { /* this flags &> redirection */
11404 np->nfile.fd = 1;
11405 pgetc(); /* this is '>', no need to check */
11406 np->type = NTO2;
11407 }
11408#endif
11409 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011410 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011411 c = pgetc();
11412 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011413 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011414 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011415 np = stzalloc(sizeof(struct nhere));
11416 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011417 }
11418 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011419 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011420 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011421 c = pgetc();
11422 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011423 heredoc->striptabs = 1;
11424 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011425 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011426 pungetc();
11427 }
11428 break;
11429
11430 case '&':
11431 np->type = NFROMFD;
11432 break;
11433
11434 case '>':
11435 np->type = NFROMTO;
11436 break;
11437
11438 default:
11439 np->type = NFROM;
11440 pungetc();
11441 break;
11442 }
Eric Andersencb57d552001-06-28 07:25:16 +000011443 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011444 if (fd >= 0)
11445 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011446 redirnode = np;
11447 goto parseredir_return;
11448}
Eric Andersencb57d552001-06-28 07:25:16 +000011449
Eric Andersencb57d552001-06-28 07:25:16 +000011450/*
11451 * Parse a substitution. At this point, we have read the dollar sign
11452 * and nothing else.
11453 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011454
11455/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11456 * (assuming ascii char codes, as the original implementation did) */
11457#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011458 (((unsigned)(c) - 33 < 32) \
11459 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011460parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011461 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011462 int typeloc;
11463 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011464
Eric Andersenc470f442003-07-28 09:56:35 +000011465 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011466 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011467 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011468 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011469#if ENABLE_ASH_BASH_COMPAT
11470 if (c == '\'')
11471 bash_dollar_squote = 1;
11472 else
11473#endif
11474 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011475 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011476 } else if (c == '(') {
11477 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011478 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011479#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011480 PARSEARITH();
11481#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011482 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011483#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011484 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011485 pungetc();
11486 PARSEBACKQNEW();
11487 }
11488 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011489 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011490 USTPUTC(CTLVAR, out);
11491 typeloc = out - (char *)stackblock();
11492 USTPUTC(VSNORMAL, out);
11493 subtype = VSNORMAL;
11494 if (c == '{') {
11495 c = pgetc();
11496 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011497 c = pgetc();
11498 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011499 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011500 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011501 subtype = VSLENGTH; /* ${#VAR} */
11502 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011503 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011504 }
Eric Andersenc470f442003-07-28 09:56:35 +000011505 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011506 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011507 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011508 do {
11509 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011510 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011511 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011512 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011513 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011514 do {
11515 STPUTC(c, out);
11516 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011517 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011518 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011519 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011520 USTPUTC(c, out);
11521 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011522 } else {
11523 badsub:
11524 raise_error_syntax("bad substitution");
11525 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011526 if (c != '}' && subtype == VSLENGTH) {
11527 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011528 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011529 }
Eric Andersencb57d552001-06-28 07:25:16 +000011530
Eric Andersenc470f442003-07-28 09:56:35 +000011531 STPUTC('=', out);
11532 flags = 0;
11533 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011534 /* ${VAR...} but not $VAR or ${#VAR} */
11535 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011536 switch (c) {
11537 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011538 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011539#if ENABLE_ASH_BASH_COMPAT
11540 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011541//TODO: support more general format ${v:EXPR:EXPR},
11542// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011543 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011544 pungetc();
11545 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011546 }
11547#endif
11548 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011549 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011550 default: {
11551 static const char types[] ALIGN1 = "}-+?=";
11552 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011553 if (p == NULL)
11554 goto badsub;
11555 subtype = p - types + VSNORMAL;
11556 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011557 }
Eric Andersenc470f442003-07-28 09:56:35 +000011558 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011559 case '#': {
11560 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011561 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011562 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011563 if (c != cc)
11564 goto do_pungetc;
11565 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011566 break;
11567 }
11568#if ENABLE_ASH_BASH_COMPAT
11569 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011570 /* ${v/[/]pattern/repl} */
11571//TODO: encode pattern and repl separately.
11572// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011573 subtype = VSREPLACE;
11574 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011575 if (c != '/')
11576 goto do_pungetc;
11577 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011578 break;
11579#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011580 }
Eric Andersenc470f442003-07-28 09:56:35 +000011581 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011582 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011583 pungetc();
11584 }
11585 if (dblquote || arinest)
11586 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011587 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011588 if (subtype != VSNORMAL) {
11589 varnest++;
11590 if (dblquote || arinest) {
11591 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011592 }
11593 }
11594 }
Eric Andersenc470f442003-07-28 09:56:35 +000011595 goto parsesub_return;
11596}
Eric Andersencb57d552001-06-28 07:25:16 +000011597
Eric Andersencb57d552001-06-28 07:25:16 +000011598/*
11599 * Called to parse command substitutions. Newstyle is set if the command
11600 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11601 * list of commands (passed by reference), and savelen is the number of
11602 * characters on the top of the stack which must be preserved.
11603 */
Eric Andersenc470f442003-07-28 09:56:35 +000011604parsebackq: {
11605 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011606 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011607 union node *n;
11608 char *volatile str;
11609 struct jmploc jmploc;
11610 struct jmploc *volatile savehandler;
11611 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011612 smallint saveprompt = 0;
11613
Eric Andersencb57d552001-06-28 07:25:16 +000011614#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011615 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011616#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011617 savepbq = parsebackquote;
11618 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011619 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011620 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011621 exception_handler = savehandler;
11622 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011623 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011624 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011625 str = NULL;
11626 savelen = out - (char *)stackblock();
11627 if (savelen > 0) {
11628 str = ckmalloc(savelen);
11629 memcpy(str, stackblock(), savelen);
11630 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011631 savehandler = exception_handler;
11632 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011633 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011634 if (oldstyle) {
11635 /* We must read until the closing backquote, giving special
11636 treatment to some slashes, and then push the string and
11637 reread it as input, interpreting it normally. */
11638 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011639 size_t psavelen;
11640 char *pstr;
11641
Eric Andersenc470f442003-07-28 09:56:35 +000011642 STARTSTACKSTR(pout);
11643 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011644 int pc;
11645
11646 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011647 pc = pgetc();
11648 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011649 case '`':
11650 goto done;
11651
11652 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011653 pc = pgetc();
11654 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011655 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011656 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011657 /*
11658 * If eating a newline, avoid putting
11659 * the newline into the new character
11660 * stream (via the STPUTC after the
11661 * switch).
11662 */
11663 continue;
11664 }
11665 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011666 && (!dblquote || pc != '"')
11667 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011668 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011669 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011670 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011671 break;
11672 }
11673 /* fall through */
11674
11675 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011676 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011677 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011678 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011679
11680 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011681 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011682 needprompt = doprompt;
11683 break;
11684
11685 default:
11686 break;
11687 }
11688 STPUTC(pc, pout);
11689 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011690 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011691 STPUTC('\0', pout);
11692 psavelen = pout - (char *)stackblock();
11693 if (psavelen > 0) {
11694 pstr = grabstackstr(pout);
11695 setinputstring(pstr);
11696 }
11697 }
11698 nlpp = &bqlist;
11699 while (*nlpp)
11700 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011701 *nlpp = stzalloc(sizeof(**nlpp));
11702 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011703 parsebackquote = oldstyle;
11704
11705 if (oldstyle) {
11706 saveprompt = doprompt;
11707 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011708 }
11709
Eric Andersenc470f442003-07-28 09:56:35 +000011710 n = list(2);
11711
11712 if (oldstyle)
11713 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011714 else if (readtoken() != TRP)
11715 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011716
11717 (*nlpp)->n = n;
11718 if (oldstyle) {
11719 /*
11720 * Start reading from old file again, ignoring any pushed back
11721 * tokens left from the backquote parsing
11722 */
11723 popfile();
11724 tokpushback = 0;
11725 }
11726 while (stackblocksize() <= savelen)
11727 growstackblock();
11728 STARTSTACKSTR(out);
11729 if (str) {
11730 memcpy(out, str, savelen);
11731 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011732 INT_OFF;
11733 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011734 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011735 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011736 }
11737 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011738 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011739 if (arinest || dblquote)
11740 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11741 else
11742 USTPUTC(CTLBACKQ, out);
11743 if (oldstyle)
11744 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011745 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011746}
11747
Mike Frysinger98c52642009-04-02 10:02:37 +000011748#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011749/*
11750 * Parse an arithmetic expansion (indicate start of one and set state)
11751 */
Eric Andersenc470f442003-07-28 09:56:35 +000011752parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011753 if (++arinest == 1) {
11754 prevsyntax = syntax;
11755 syntax = ARISYNTAX;
11756 USTPUTC(CTLARI, out);
11757 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011758 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011759 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011760 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011761 } else {
11762 /*
11763 * we collapse embedded arithmetic expansion to
11764 * parenthesis, which should be equivalent
11765 */
11766 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011767 }
Eric Andersenc470f442003-07-28 09:56:35 +000011768 goto parsearith_return;
11769}
11770#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011771
Eric Andersenc470f442003-07-28 09:56:35 +000011772} /* end of readtoken */
11773
Eric Andersencb57d552001-06-28 07:25:16 +000011774/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011775 * Read the next input token.
11776 * If the token is a word, we set backquotelist to the list of cmds in
11777 * backquotes. We set quoteflag to true if any part of the word was
11778 * quoted.
11779 * If the token is TREDIR, then we set redirnode to a structure containing
11780 * the redirection.
11781 * In all cases, the variable startlinno is set to the number of the line
11782 * on which the token starts.
11783 *
11784 * [Change comment: here documents and internal procedures]
11785 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11786 * word parsing code into a separate routine. In this case, readtoken
11787 * doesn't need to have any internal procedures, but parseword does.
11788 * We could also make parseoperator in essence the main routine, and
11789 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011790 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011791#define NEW_xxreadtoken
11792#ifdef NEW_xxreadtoken
11793/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011794static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011795 '\n', '(', ')', /* singles */
11796 '&', '|', ';', /* doubles */
11797 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011798};
Eric Andersencb57d552001-06-28 07:25:16 +000011799
Denis Vlasenko834dee72008-10-07 09:18:30 +000011800#define xxreadtoken_singles 3
11801#define xxreadtoken_doubles 3
11802
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011803static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011804 TNL, TLP, TRP, /* only single occurrence allowed */
11805 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11806 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011807 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011808};
11809
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011810static int
11811xxreadtoken(void)
11812{
11813 int c;
11814
11815 if (tokpushback) {
11816 tokpushback = 0;
11817 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011818 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011819 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011820 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011821 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011822 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011823 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011824 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011825
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011826 if (c == '#') {
11827 while ((c = pgetc()) != '\n' && c != PEOF)
11828 continue;
11829 pungetc();
11830 } else if (c == '\\') {
11831 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011832 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011833 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011834 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011835 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011836 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011837 } else {
11838 const char *p;
11839
11840 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11841 if (c != PEOF) {
11842 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011843 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011844 needprompt = doprompt;
11845 }
11846
11847 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011848 if (p == NULL)
11849 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011850
Denis Vlasenko834dee72008-10-07 09:18:30 +000011851 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11852 int cc = pgetc();
11853 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011854 p += xxreadtoken_doubles + 1;
11855 } else {
11856 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011857#if ENABLE_ASH_BASH_COMPAT
11858 if (c == '&' && cc == '>') /* &> */
11859 break; /* return readtoken1(...) */
11860#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011861 }
11862 }
11863 }
11864 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11865 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011866 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011867 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011868
11869 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011870}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011871#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011872#define RETURN(token) return lasttoken = token
11873static int
11874xxreadtoken(void)
11875{
11876 int c;
11877
11878 if (tokpushback) {
11879 tokpushback = 0;
11880 return lasttoken;
11881 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011882 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011883 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011884 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011885 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011886 switch (c) {
11887 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011888 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011889 continue;
11890 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011891 while ((c = pgetc()) != '\n' && c != PEOF)
11892 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011893 pungetc();
11894 continue;
11895 case '\\':
11896 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011897 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011898 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011899 continue;
11900 }
11901 pungetc();
11902 goto breakloop;
11903 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011904 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011905 needprompt = doprompt;
11906 RETURN(TNL);
11907 case PEOF:
11908 RETURN(TEOF);
11909 case '&':
11910 if (pgetc() == '&')
11911 RETURN(TAND);
11912 pungetc();
11913 RETURN(TBACKGND);
11914 case '|':
11915 if (pgetc() == '|')
11916 RETURN(TOR);
11917 pungetc();
11918 RETURN(TPIPE);
11919 case ';':
11920 if (pgetc() == ';')
11921 RETURN(TENDCASE);
11922 pungetc();
11923 RETURN(TSEMI);
11924 case '(':
11925 RETURN(TLP);
11926 case ')':
11927 RETURN(TRP);
11928 default:
11929 goto breakloop;
11930 }
11931 }
11932 breakloop:
11933 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11934#undef RETURN
11935}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011936#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011937
11938static int
11939readtoken(void)
11940{
11941 int t;
11942#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011943 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011944#endif
11945
11946#if ENABLE_ASH_ALIAS
11947 top:
11948#endif
11949
11950 t = xxreadtoken();
11951
11952 /*
11953 * eat newlines
11954 */
11955 if (checkkwd & CHKNL) {
11956 while (t == TNL) {
11957 parseheredoc();
11958 t = xxreadtoken();
11959 }
11960 }
11961
11962 if (t != TWORD || quoteflag) {
11963 goto out;
11964 }
11965
11966 /*
11967 * check for keywords
11968 */
11969 if (checkkwd & CHKKWD) {
11970 const char *const *pp;
11971
11972 pp = findkwd(wordtext);
11973 if (pp) {
11974 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011975 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011976 goto out;
11977 }
11978 }
11979
11980 if (checkkwd & CHKALIAS) {
11981#if ENABLE_ASH_ALIAS
11982 struct alias *ap;
11983 ap = lookupalias(wordtext, 1);
11984 if (ap != NULL) {
11985 if (*ap->val) {
11986 pushstring(ap->val, ap);
11987 }
11988 goto top;
11989 }
11990#endif
11991 }
11992 out:
11993 checkkwd = 0;
11994#if DEBUG
11995 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011996 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011997 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011998 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011999#endif
12000 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000012001}
12002
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012003static char
12004peektoken(void)
12005{
12006 int t;
12007
12008 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012009 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012010 return tokname_array[t][0];
12011}
Eric Andersencb57d552001-06-28 07:25:16 +000012012
12013/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012014 * Read and parse a command. Returns NODE_EOF on end of file.
12015 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000012016 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012017static union node *
12018parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000012019{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012020 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000012021
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012022 tokpushback = 0;
12023 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012024 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012025 needprompt = 0;
12026 t = readtoken();
12027 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012028 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012029 if (t == TNL)
12030 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012031 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012032 return list(1);
12033}
12034
12035/*
12036 * Input any here documents.
12037 */
12038static void
12039parseheredoc(void)
12040{
12041 struct heredoc *here;
12042 union node *n;
12043
12044 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012045 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012046
12047 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012048 setprompt_if(needprompt, 2);
12049 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012050 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012051 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012052 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012053 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012054 n->narg.text = wordtext;
12055 n->narg.backquote = backquotelist;
12056 here->here->nhere.doc = n;
12057 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000012058 }
Eric Andersencb57d552001-06-28 07:25:16 +000012059}
12060
12061
12062/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012063 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000012064 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000012065#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012066static const char *
12067expandstr(const char *ps)
12068{
12069 union node n;
12070
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000012071 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
12072 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012073 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000012074 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000012075 popfile();
12076
12077 n.narg.type = NARG;
12078 n.narg.next = NULL;
12079 n.narg.text = wordtext;
12080 n.narg.backquote = backquotelist;
12081
12082 expandarg(&n, NULL, 0);
12083 return stackblock();
12084}
12085#endif
12086
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012087/*
12088 * Execute a command or commands contained in a string.
12089 */
12090static int
12091evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000012092{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012093 union node *n;
12094 struct stackmark smark;
12095 int skip;
12096
12097 setinputstring(s);
12098 setstackmark(&smark);
12099
12100 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012101 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012102 evaltree(n, 0);
12103 popstackmark(&smark);
12104 skip = evalskip;
12105 if (skip)
12106 break;
12107 }
12108 popfile();
12109
12110 skip &= mask;
12111 evalskip = skip;
12112 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012113}
12114
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012115/*
12116 * The eval command.
12117 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012118static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012119evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012120{
12121 char *p;
12122 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012123
Denis Vlasenko68404f12008-03-17 09:00:54 +000012124 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012125 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012126 argv += 2;
12127 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012128 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012129 for (;;) {
12130 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012131 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012132 if (p == NULL)
12133 break;
12134 STPUTC(' ', concat);
12135 }
12136 STPUTC('\0', concat);
12137 p = grabstackstr(concat);
12138 }
12139 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012140 }
12141 return exitstatus;
12142}
12143
12144/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012145 * Read and execute commands.
12146 * "Top" is nonzero for the top level command loop;
12147 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012148 */
12149static int
12150cmdloop(int top)
12151{
12152 union node *n;
12153 struct stackmark smark;
12154 int inter;
12155 int numeof = 0;
12156
12157 TRACE(("cmdloop(%d) called\n", top));
12158 for (;;) {
12159 int skip;
12160
12161 setstackmark(&smark);
12162#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012163 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012164 showjobs(stderr, SHOW_CHANGED);
12165#endif
12166 inter = 0;
12167 if (iflag && top) {
12168 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012169 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012170 }
12171 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012172#if DEBUG
12173 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012174 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012175#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012176 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012177 if (!top || numeof >= 50)
12178 break;
12179 if (!stoppedjobs()) {
12180 if (!Iflag)
12181 break;
12182 out2str("\nUse \"exit\" to leave shell.\n");
12183 }
12184 numeof++;
12185 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012186 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12187 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012188 numeof = 0;
12189 evaltree(n, 0);
12190 }
12191 popstackmark(&smark);
12192 skip = evalskip;
12193
12194 if (skip) {
12195 evalskip = 0;
12196 return skip & SKIPEVAL;
12197 }
12198 }
12199 return 0;
12200}
12201
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012202/*
12203 * Take commands from a file. To be compatible we should do a path
12204 * search for the file, which is necessary to find sub-commands.
12205 */
12206static char *
12207find_dot_file(char *name)
12208{
12209 char *fullname;
12210 const char *path = pathval();
12211 struct stat statb;
12212
12213 /* don't try this for absolute or relative paths */
12214 if (strchr(name, '/'))
12215 return name;
12216
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012217 /* IIRC standards do not say whether . is to be searched.
12218 * And it is even smaller this way, making it unconditional for now:
12219 */
12220 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12221 fullname = name;
12222 goto try_cur_dir;
12223 }
12224
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012225 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012226 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012227 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12228 /*
12229 * Don't bother freeing here, since it will
12230 * be freed by the caller.
12231 */
12232 return fullname;
12233 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012234 if (fullname != name)
12235 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012236 }
12237
12238 /* not found in the PATH */
12239 ash_msg_and_raise_error("%s: not found", name);
12240 /* NOTREACHED */
12241}
12242
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012243static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012244dotcmd(int argc, char **argv)
12245{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012246 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012247 struct strlist *sp;
12248 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012249
12250 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012251 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012252
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012253 if (!argv[1]) {
12254 /* bash says: "bash: .: filename argument required" */
12255 return 2; /* bash compat */
12256 }
12257
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012258 /* "false; . empty_file; echo $?" should print 0, not 1: */
12259 exitstatus = 0;
12260
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012261 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012262
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012263 argv += 2;
12264 argc -= 2;
12265 if (argc) { /* argc > 0, argv[0] != NULL */
12266 saveparam = shellparam;
12267 shellparam.malloced = 0;
12268 shellparam.nparam = argc;
12269 shellparam.p = argv;
12270 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012271
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012272 setinputfile(fullname, INPUT_PUSH_FILE);
12273 commandname = fullname;
12274 cmdloop(0);
12275 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012276
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012277 if (argc) {
12278 freeparam(&shellparam);
12279 shellparam = saveparam;
12280 };
12281
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012282 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012283}
12284
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012285static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012286exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012287{
12288 if (stoppedjobs())
12289 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012290 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012291 exitstatus = number(argv[1]);
12292 raise_exception(EXEXIT);
12293 /* NOTREACHED */
12294}
12295
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012296/*
12297 * Read a file containing shell functions.
12298 */
12299static void
12300readcmdfile(char *name)
12301{
12302 setinputfile(name, INPUT_PUSH_FILE);
12303 cmdloop(0);
12304 popfile();
12305}
12306
12307
Denis Vlasenkocc571512007-02-23 21:10:35 +000012308/* ============ find_command inplementation */
12309
12310/*
12311 * Resolve a command name. If you change this routine, you may have to
12312 * change the shellexec routine as well.
12313 */
12314static void
12315find_command(char *name, struct cmdentry *entry, int act, const char *path)
12316{
12317 struct tblentry *cmdp;
12318 int idx;
12319 int prev;
12320 char *fullname;
12321 struct stat statb;
12322 int e;
12323 int updatetbl;
12324 struct builtincmd *bcmd;
12325
12326 /* If name contains a slash, don't use PATH or hash table */
12327 if (strchr(name, '/') != NULL) {
12328 entry->u.index = -1;
12329 if (act & DO_ABS) {
12330 while (stat(name, &statb) < 0) {
12331#ifdef SYSV
12332 if (errno == EINTR)
12333 continue;
12334#endif
12335 entry->cmdtype = CMDUNKNOWN;
12336 return;
12337 }
12338 }
12339 entry->cmdtype = CMDNORMAL;
12340 return;
12341 }
12342
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012343/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012344
12345 updatetbl = (path == pathval());
12346 if (!updatetbl) {
12347 act |= DO_ALTPATH;
12348 if (strstr(path, "%builtin") != NULL)
12349 act |= DO_ALTBLTIN;
12350 }
12351
12352 /* If name is in the table, check answer will be ok */
12353 cmdp = cmdlookup(name, 0);
12354 if (cmdp != NULL) {
12355 int bit;
12356
12357 switch (cmdp->cmdtype) {
12358 default:
12359#if DEBUG
12360 abort();
12361#endif
12362 case CMDNORMAL:
12363 bit = DO_ALTPATH;
12364 break;
12365 case CMDFUNCTION:
12366 bit = DO_NOFUNC;
12367 break;
12368 case CMDBUILTIN:
12369 bit = DO_ALTBLTIN;
12370 break;
12371 }
12372 if (act & bit) {
12373 updatetbl = 0;
12374 cmdp = NULL;
12375 } else if (cmdp->rehash == 0)
12376 /* if not invalidated by cd, we're done */
12377 goto success;
12378 }
12379
12380 /* If %builtin not in path, check for builtin next */
12381 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012382 if (bcmd) {
12383 if (IS_BUILTIN_REGULAR(bcmd))
12384 goto builtin_success;
12385 if (act & DO_ALTPATH) {
12386 if (!(act & DO_ALTBLTIN))
12387 goto builtin_success;
12388 } else if (builtinloc <= 0) {
12389 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012390 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012391 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012392
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012393#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012394 {
12395 int applet_no = find_applet_by_name(name);
12396 if (applet_no >= 0) {
12397 entry->cmdtype = CMDNORMAL;
12398 entry->u.index = -2 - applet_no;
12399 return;
12400 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012401 }
12402#endif
12403
Denis Vlasenkocc571512007-02-23 21:10:35 +000012404 /* We have to search path. */
12405 prev = -1; /* where to start */
12406 if (cmdp && cmdp->rehash) { /* doing a rehash */
12407 if (cmdp->cmdtype == CMDBUILTIN)
12408 prev = builtinloc;
12409 else
12410 prev = cmdp->param.index;
12411 }
12412
12413 e = ENOENT;
12414 idx = -1;
12415 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012416 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012417 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012418 /* NB: code below will still use fullname
12419 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012420 idx++;
12421 if (pathopt) {
12422 if (prefix(pathopt, "builtin")) {
12423 if (bcmd)
12424 goto builtin_success;
12425 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012426 }
12427 if ((act & DO_NOFUNC)
12428 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012429 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012430 continue;
12431 }
12432 }
12433 /* if rehash, don't redo absolute path names */
12434 if (fullname[0] == '/' && idx <= prev) {
12435 if (idx < prev)
12436 continue;
12437 TRACE(("searchexec \"%s\": no change\n", name));
12438 goto success;
12439 }
12440 while (stat(fullname, &statb) < 0) {
12441#ifdef SYSV
12442 if (errno == EINTR)
12443 continue;
12444#endif
12445 if (errno != ENOENT && errno != ENOTDIR)
12446 e = errno;
12447 goto loop;
12448 }
12449 e = EACCES; /* if we fail, this will be the error */
12450 if (!S_ISREG(statb.st_mode))
12451 continue;
12452 if (pathopt) { /* this is a %func directory */
12453 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012454 /* NB: stalloc will return space pointed by fullname
12455 * (because we don't have any intervening allocations
12456 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012457 readcmdfile(fullname);
12458 cmdp = cmdlookup(name, 0);
12459 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12460 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12461 stunalloc(fullname);
12462 goto success;
12463 }
12464 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12465 if (!updatetbl) {
12466 entry->cmdtype = CMDNORMAL;
12467 entry->u.index = idx;
12468 return;
12469 }
12470 INT_OFF;
12471 cmdp = cmdlookup(name, 1);
12472 cmdp->cmdtype = CMDNORMAL;
12473 cmdp->param.index = idx;
12474 INT_ON;
12475 goto success;
12476 }
12477
12478 /* We failed. If there was an entry for this command, delete it */
12479 if (cmdp && updatetbl)
12480 delete_cmd_entry();
12481 if (act & DO_ERR)
12482 ash_msg("%s: %s", name, errmsg(e, "not found"));
12483 entry->cmdtype = CMDUNKNOWN;
12484 return;
12485
12486 builtin_success:
12487 if (!updatetbl) {
12488 entry->cmdtype = CMDBUILTIN;
12489 entry->u.cmd = bcmd;
12490 return;
12491 }
12492 INT_OFF;
12493 cmdp = cmdlookup(name, 1);
12494 cmdp->cmdtype = CMDBUILTIN;
12495 cmdp->param.cmd = bcmd;
12496 INT_ON;
12497 success:
12498 cmdp->rehash = 0;
12499 entry->cmdtype = cmdp->cmdtype;
12500 entry->u = cmdp->param;
12501}
12502
12503
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012504/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012505
Eric Andersencb57d552001-06-28 07:25:16 +000012506/*
Eric Andersencb57d552001-06-28 07:25:16 +000012507 * The trap builtin.
12508 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012509static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012510trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012511{
12512 char *action;
12513 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012514 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012515
Eric Andersenc470f442003-07-28 09:56:35 +000012516 nextopt(nullstr);
12517 ap = argptr;
12518 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012519 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012520 char *tr = trap_ptr[signo];
12521 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012522 /* note: bash adds "SIG", but only if invoked
12523 * as "bash". If called as "sh", or if set -o posix,
12524 * then it prints short signal names.
12525 * We are printing short names: */
12526 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012527 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012528 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012529 /* trap_ptr != trap only if we are in special-cased `trap` code.
12530 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012531 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012532 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012533 }
12534 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012535 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012536 if (trap_ptr != trap) {
12537 free(trap_ptr);
12538 trap_ptr = trap;
12539 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012540 */
Eric Andersencb57d552001-06-28 07:25:16 +000012541 return 0;
12542 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012543
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012544 action = NULL;
12545 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012546 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012547 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012548 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012549 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012550 if (signo < 0) {
12551 /* Mimic bash message exactly */
12552 ash_msg("%s: invalid signal specification", *ap);
12553 exitcode = 1;
12554 goto next;
12555 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012556 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012557 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012558 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012559 action = NULL;
12560 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012561 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012562 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012563 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012564 if (action)
12565 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012566 trap[signo] = action;
12567 if (signo != 0)
12568 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012569 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012570 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012571 ap++;
12572 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012573 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012574}
12575
Eric Andersenc470f442003-07-28 09:56:35 +000012576
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012577/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012578
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012579#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012580/*
12581 * Lists available builtins
12582 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012583static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012584helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012585{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012586 unsigned col;
12587 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012588
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012589 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012590 "Built-in commands:\n"
12591 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012592 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012593 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012594 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012595 if (col > 60) {
12596 out1fmt("\n");
12597 col = 0;
12598 }
12599 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012600#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012601 {
12602 const char *a = applet_names;
12603 while (*a) {
12604 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12605 if (col > 60) {
12606 out1fmt("\n");
12607 col = 0;
12608 }
12609 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012610 }
12611 }
12612#endif
12613 out1fmt("\n\n");
12614 return EXIT_SUCCESS;
12615}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012616#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012617
Eric Andersencb57d552001-06-28 07:25:16 +000012618/*
Eric Andersencb57d552001-06-28 07:25:16 +000012619 * The export and readonly commands.
12620 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012621static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012622exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012623{
12624 struct var *vp;
12625 char *name;
12626 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012627 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012628 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012629
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012630 if (nextopt("p") != 'p') {
12631 aptr = argptr;
12632 name = *aptr;
12633 if (name) {
12634 do {
12635 p = strchr(name, '=');
12636 if (p != NULL) {
12637 p++;
12638 } else {
12639 vp = *findvar(hashvar(name), name);
12640 if (vp) {
12641 vp->flags |= flag;
12642 continue;
12643 }
Eric Andersencb57d552001-06-28 07:25:16 +000012644 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012645 setvar(name, p, flag);
12646 } while ((name = *++aptr) != NULL);
12647 return 0;
12648 }
Eric Andersencb57d552001-06-28 07:25:16 +000012649 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012650 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012651 return 0;
12652}
12653
Eric Andersencb57d552001-06-28 07:25:16 +000012654/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012655 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012656 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012657static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012658unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012659{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012660 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012661
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012662 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012663 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012664 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012665}
12666
Eric Andersencb57d552001-06-28 07:25:16 +000012667/*
Eric Andersencb57d552001-06-28 07:25:16 +000012668 * The unset builtin command. We unset the function before we unset the
12669 * variable to allow a function to be unset when there is a readonly variable
12670 * with the same name.
12671 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012672static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012673unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012674{
12675 char **ap;
12676 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012677 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012678 int ret = 0;
12679
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012680 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012681 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012682 }
Eric Andersencb57d552001-06-28 07:25:16 +000012683
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012684 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012685 if (flag != 'f') {
12686 i = unsetvar(*ap);
12687 ret |= i;
12688 if (!(i & 2))
12689 continue;
12690 }
12691 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012692 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012693 }
Eric Andersenc470f442003-07-28 09:56:35 +000012694 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012695}
12696
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012697static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012698 ' ', offsetof(struct tms, tms_utime),
12699 '\n', offsetof(struct tms, tms_stime),
12700 ' ', offsetof(struct tms, tms_cutime),
12701 '\n', offsetof(struct tms, tms_cstime),
12702 0
12703};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012704static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012705timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012706{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012707 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012708 const unsigned char *p;
12709 struct tms buf;
12710
12711 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012712 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012713
12714 p = timescmd_str;
12715 do {
12716 t = *(clock_t *)(((char *) &buf) + p[1]);
12717 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012718 t = t % clk_tck;
12719 out1fmt("%lum%lu.%03lus%c",
12720 s / 60, s % 60,
12721 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012722 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012723 p += 2;
12724 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012725
Eric Andersencb57d552001-06-28 07:25:16 +000012726 return 0;
12727}
12728
Mike Frysinger98c52642009-04-02 10:02:37 +000012729#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012730/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012731 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012732 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012733 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012734 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012735 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012736static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012737letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012738{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012739 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012740
Denis Vlasenko68404f12008-03-17 09:00:54 +000012741 argv++;
12742 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012743 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012744 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012745 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012746 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012747
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012748 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012749}
Eric Andersenc470f442003-07-28 09:56:35 +000012750#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012751
Eric Andersenc470f442003-07-28 09:56:35 +000012752/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012753 * The read builtin. Options:
12754 * -r Do not interpret '\' specially
12755 * -s Turn off echo (tty only)
12756 * -n NCHARS Read NCHARS max
12757 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12758 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12759 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012760 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012761 * TODO: bash also has:
12762 * -a ARRAY Read into array[0],[1],etc
12763 * -d DELIM End on DELIM char, not newline
12764 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012765 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012766static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012767readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012768{
Denys Vlasenko73067272010-01-12 22:11:24 +010012769 char *opt_n = NULL;
12770 char *opt_p = NULL;
12771 char *opt_t = NULL;
12772 char *opt_u = NULL;
12773 int read_flags = 0;
12774 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012775 int i;
12776
Denys Vlasenko73067272010-01-12 22:11:24 +010012777 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012778 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012779 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012780 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012781 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012782 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012783 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012784 break;
12785 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012786 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012787 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012788 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012789 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012790 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012791 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012792 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012793 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012794 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012795 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012796 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012797 default:
12798 break;
12799 }
Eric Andersenc470f442003-07-28 09:56:35 +000012800 }
Paul Fox02eb9342005-09-07 16:56:02 +000012801
Denys Vlasenko03dad222010-01-12 23:29:57 +010012802 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012803 argptr,
12804 bltinlookup("IFS"), /* can be NULL */
12805 read_flags,
12806 opt_n,
12807 opt_p,
12808 opt_t,
12809 opt_u
12810 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012811
Denys Vlasenko73067272010-01-12 22:11:24 +010012812 if ((uintptr_t)r > 1)
12813 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012814
Denys Vlasenko73067272010-01-12 22:11:24 +010012815 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012816}
12817
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012818static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012819umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012820{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012821 static const char permuser[3] ALIGN1 = "ugo";
12822 static const char permmode[3] ALIGN1 = "rwx";
12823 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012824 S_IRUSR, S_IWUSR, S_IXUSR,
12825 S_IRGRP, S_IWGRP, S_IXGRP,
12826 S_IROTH, S_IWOTH, S_IXOTH
12827 };
12828
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012829 /* TODO: use bb_parse_mode() instead */
12830
Eric Andersenc470f442003-07-28 09:56:35 +000012831 char *ap;
12832 mode_t mask;
12833 int i;
12834 int symbolic_mode = 0;
12835
12836 while (nextopt("S") != '\0') {
12837 symbolic_mode = 1;
12838 }
12839
Denis Vlasenkob012b102007-02-19 22:43:01 +000012840 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012841 mask = umask(0);
12842 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012843 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012844
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012845 ap = *argptr;
12846 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012847 if (symbolic_mode) {
12848 char buf[18];
12849 char *p = buf;
12850
12851 for (i = 0; i < 3; i++) {
12852 int j;
12853
12854 *p++ = permuser[i];
12855 *p++ = '=';
12856 for (j = 0; j < 3; j++) {
12857 if ((mask & permmask[3 * i + j]) == 0) {
12858 *p++ = permmode[j];
12859 }
12860 }
12861 *p++ = ',';
12862 }
12863 *--p = 0;
12864 puts(buf);
12865 } else {
12866 out1fmt("%.4o\n", mask);
12867 }
12868 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012869 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012870 mask = 0;
12871 do {
12872 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012873 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012874 mask = (mask << 3) + (*ap - '0');
12875 } while (*++ap != '\0');
12876 umask(mask);
12877 } else {
12878 mask = ~mask & 0777;
12879 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012880 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012881 }
12882 umask(~mask & 0777);
12883 }
12884 }
12885 return 0;
12886}
12887
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012888static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012889ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012890{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012891 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012892}
12893
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012894/* ============ main() and helpers */
12895
12896/*
12897 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012898 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012899static void
12900exitshell(void)
12901{
12902 struct jmploc loc;
12903 char *p;
12904 int status;
12905
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020012906#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
12907 save_history(line_input_state);
12908#endif
12909
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012910 status = exitstatus;
12911 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12912 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012913 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012914/* dash bug: it just does _exit(exitstatus) here
12915 * but we have to do setjobctl(0) first!
12916 * (bug is still not fixed in dash-0.5.3 - if you run dash
12917 * under Midnight Commander, on exit from dash MC is backgrounded) */
12918 status = exitstatus;
12919 goto out;
12920 }
12921 exception_handler = &loc;
12922 p = trap[0];
12923 if (p) {
12924 trap[0] = NULL;
12925 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012926 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012927 }
12928 flush_stdout_stderr();
12929 out:
12930 setjobctl(0);
12931 _exit(status);
12932 /* NOTREACHED */
12933}
12934
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012935static void
12936init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012937{
12938 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012939 /* we will never free this */
12940 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012941
12942 /* from trap.c: */
12943 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012944 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12945 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12946 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012947 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012948
12949 /* from var.c: */
12950 {
12951 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012952 const char *p;
12953 struct stat st1, st2;
12954
12955 initvar();
12956 for (envp = environ; envp && *envp; envp++) {
12957 if (strchr(*envp, '=')) {
12958 setvareq(*envp, VEXPORT|VTEXTFIXED);
12959 }
12960 }
12961
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012962 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012963
12964 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012965 if (p) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012966 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012967 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
12968 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012969 p = '\0';
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012970 }
12971 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012972 setpwd(p, 0);
12973 }
12974}
12975
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012976
12977//usage:#define ash_trivial_usage
Denys Vlasenko6b6af532011-03-08 10:24:17 +010012978//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
Denys Vlasenkob0b83432011-03-07 12:34:59 +010012979//usage:#define ash_full_usage "\n\n"
12980//usage: "Unix shell interpreter"
12981
12982//usage:#if ENABLE_FEATURE_SH_IS_ASH
12983//usage:# define sh_trivial_usage ash_trivial_usage
12984//usage:# define sh_full_usage ash_full_usage
12985//usage:#endif
12986//usage:#if ENABLE_FEATURE_BASH_IS_ASH
12987//usage:# define bash_trivial_usage ash_trivial_usage
12988//usage:# define bash_full_usage ash_full_usage
12989//usage:#endif
12990
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012991/*
12992 * Process the shell command line arguments.
12993 */
12994static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012995procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012996{
12997 int i;
12998 const char *xminusc;
12999 char **xargv;
13000
13001 xargv = argv;
13002 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013003 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013004 xargv++;
13005 for (i = 0; i < NOPTS; i++)
13006 optlist[i] = 2;
13007 argptr = xargv;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010013008 if (options(/*cmdline:*/ 1)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000013009 /* it already printed err message */
13010 raise_exception(EXERROR);
13011 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013012 xargv = argptr;
13013 xminusc = minusc;
13014 if (*xargv == NULL) {
13015 if (xminusc)
13016 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
13017 sflag = 1;
13018 }
13019 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
13020 iflag = 1;
13021 if (mflag == 2)
13022 mflag = iflag;
13023 for (i = 0; i < NOPTS; i++)
13024 if (optlist[i] == 2)
13025 optlist[i] = 0;
13026#if DEBUG == 2
13027 debug = 1;
13028#endif
13029 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
13030 if (xminusc) {
13031 minusc = *xargv++;
13032 if (*xargv)
13033 goto setarg0;
13034 } else if (!sflag) {
13035 setinputfile(*xargv, 0);
13036 setarg0:
13037 arg0 = *xargv++;
13038 commandname = arg0;
13039 }
13040
13041 shellparam.p = xargv;
13042#if ENABLE_ASH_GETOPTS
13043 shellparam.optind = 1;
13044 shellparam.optoff = -1;
13045#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013046 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013047 while (*xargv) {
13048 shellparam.nparam++;
13049 xargv++;
13050 }
13051 optschanged();
13052}
13053
13054/*
13055 * Read /etc/profile or .profile.
13056 */
13057static void
13058read_profile(const char *name)
13059{
13060 int skip;
13061
13062 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
13063 return;
13064 skip = cmdloop(0);
13065 popfile();
13066 if (skip)
13067 exitshell();
13068}
13069
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013070/*
13071 * This routine is called when an error or an interrupt occurs in an
13072 * interactive shell and control is returned to the main command loop.
13073 */
13074static void
13075reset(void)
13076{
13077 /* from eval.c: */
13078 evalskip = 0;
13079 loopnest = 0;
13080 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000013081 g_parsefile->left_in_buffer = 0;
13082 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013083 popallfiles();
13084 /* from parser.c: */
13085 tokpushback = 0;
13086 checkkwd = 0;
13087 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000013088 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013089}
13090
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013091#if PROFILE
13092static short profile_buf[16384];
13093extern int etext();
13094#endif
13095
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013096/*
13097 * Main routine. We initialize things, parse the arguments, execute
13098 * profiles if we're a login shell, and then call cmdloop to execute
13099 * commands. The setjmp call sets up the location to jump to when an
13100 * exception occurs. When an exception occurs the variable "state"
13101 * is used to figure out how far we had gotten.
13102 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000013103int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013104int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013105{
Mike Frysinger98c52642009-04-02 10:02:37 +000013106 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013107 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013108 struct jmploc jmploc;
13109 struct stackmark smark;
13110
Denis Vlasenko01631112007-12-16 17:20:38 +000013111 /* Initialize global data */
13112 INIT_G_misc();
13113 INIT_G_memstack();
13114 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013115#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013116 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013117#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013118 INIT_G_cmdtable();
13119
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013120#if PROFILE
13121 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13122#endif
13123
13124#if ENABLE_FEATURE_EDITING
13125 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13126#endif
13127 state = 0;
13128 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013129 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013130 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013131
13132 reset();
13133
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013134 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013135 if (e == EXERROR)
13136 exitstatus = 2;
13137 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013138 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013139 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013140 }
13141 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013142 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013143 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013144
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013145 popstackmark(&smark);
13146 FORCE_INT_ON; /* enable interrupts */
13147 if (s == 1)
13148 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013149 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013150 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013151 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013152 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013153 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013154 }
13155 exception_handler = &jmploc;
13156#if DEBUG
13157 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013158 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013159 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013160#endif
13161 rootpid = getpid();
13162
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013163 init();
13164 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013165 procargs(argv);
13166
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013167#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13168 if (iflag) {
13169 const char *hp = lookupvar("HISTFILE");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013170 if (!hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013171 hp = lookupvar("HOME");
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013172 if (hp) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013173 char *defhp = concat_path_file(hp, ".ash_history");
13174 setvar("HISTFILE", defhp, 0);
13175 free(defhp);
13176 }
13177 }
13178 }
13179#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013180 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013181 isloginsh = 1;
13182 if (isloginsh) {
13183 state = 1;
13184 read_profile("/etc/profile");
13185 state1:
13186 state = 2;
13187 read_profile(".profile");
13188 }
13189 state2:
13190 state = 3;
13191 if (
13192#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013193 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013194#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013195 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013196 ) {
13197 shinit = lookupvar("ENV");
13198 if (shinit != NULL && *shinit != '\0') {
13199 read_profile(shinit);
13200 }
13201 }
13202 state3:
13203 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013204 if (minusc) {
13205 /* evalstring pushes parsefile stack.
13206 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013207 * is one of stacked source fds.
13208 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013209 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013210 // ^^ not necessary since now we special-case fd 0
13211 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013212 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013213 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013214
13215 if (sflag || minusc == NULL) {
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020013216#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013217 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013218 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013219 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013220 line_input_state->hist_file = hp;
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020013221# if ENABLE_FEATURE_SH_HISTFILESIZE
13222 hp = lookupvar("HISTFILESIZE");
13223 line_input_state->max_history = size_from_HISTFILESIZE(hp);
13224# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013225 }
13226#endif
13227 state4: /* XXX ??? - why isn't this before the "if" statement */
13228 cmdloop(1);
13229 }
13230#if PROFILE
13231 monitor(0);
13232#endif
13233#ifdef GPROF
13234 {
13235 extern void _mcleanup(void);
13236 _mcleanup();
13237 }
13238#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013239 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013240 exitshell();
13241 /* NOTREACHED */
13242}
13243
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013244
Eric Andersendf82f612001-06-28 07:46:40 +000013245/*-
13246 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013247 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013248 *
13249 * This code is derived from software contributed to Berkeley by
13250 * Kenneth Almquist.
13251 *
13252 * Redistribution and use in source and binary forms, with or without
13253 * modification, are permitted provided that the following conditions
13254 * are met:
13255 * 1. Redistributions of source code must retain the above copyright
13256 * notice, this list of conditions and the following disclaimer.
13257 * 2. Redistributions in binary form must reproduce the above copyright
13258 * notice, this list of conditions and the following disclaimer in the
13259 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013260 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013261 * may be used to endorse or promote products derived from this software
13262 * without specific prior written permission.
13263 *
13264 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13265 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13266 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13267 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13268 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13269 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13270 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13271 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13272 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13273 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13274 * SUCH DAMAGE.
13275 */