blob: 0e27a073ce4509b02537ce7f709b034823c25fda [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
18
Eric Andersenc470f442003-07-28 09:56:35 +000019/*
Denis Vlasenko653d8e72009-03-19 21:59:35 +000020 * The following should be set to reflect the type of system you have:
Eric Andersenc470f442003-07-28 09:56:35 +000021 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
22 * define SYSV if you are running under System V.
23 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
24 * define DEBUG=2 to compile in and turn on debugging.
25 *
26 * When debugging is on, debugging info will be written to ./trace and
27 * a quit signal will generate a core dump.
28 */
Denis Vlasenkof1733952009-03-19 23:21:55 +000029#define DEBUG 0
Denis Vlasenko653d8e72009-03-19 21:59:35 +000030/* Tweak debug output verbosity here */
31#define DEBUG_TIME 0
32#define DEBUG_PID 1
33#define DEBUG_SIG 1
34
Eric Andersenc470f442003-07-28 09:56:35 +000035#define PROFILE 0
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000036
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000037#define JOBS ENABLE_ASH_JOB_CONTROL
Eric Andersenc470f442003-07-28 09:56:35 +000038
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000039#include "busybox.h" /* for applet_names */
Denis Vlasenkob012b102007-02-19 22:43:01 +000040#include <paths.h>
41#include <setjmp.h>
42#include <fnmatch.h>
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020043#include <sys/times.h>
Denys Vlasenko73067272010-01-12 22:11:24 +010044
45#include "shell_common.h"
Denys Vlasenko26777aa2010-11-22 23:49:10 +010046#if ENABLE_SH_MATH_SUPPORT
47# include "math.h"
48#endif
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020049#if ENABLE_ASH_RANDOM_SUPPORT
50# include "random.h"
Denys Vlasenko36df0482009-10-19 16:07:28 +020051#else
52# define CLEAR_RANDOM_T(rnd) ((void)0)
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020053#endif
Denis Vlasenko61befda2008-11-25 01:36:03 +000054
Denys Vlasenko1fcbff22010-06-26 02:40:08 +020055#include "NUM_APPLETS.h"
Denys Vlasenko14974842010-03-23 01:08:26 +010056#if NUM_APPLETS == 1
Denis Vlasenko61befda2008-11-25 01:36:03 +000057/* STANDALONE does not make sense, and won't compile */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020058# undef CONFIG_FEATURE_SH_STANDALONE
59# undef ENABLE_FEATURE_SH_STANDALONE
60# undef IF_FEATURE_SH_STANDALONE
Denys Vlasenko14974842010-03-23 01:08:26 +010061# undef IF_NOT_FEATURE_SH_STANDALONE
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020062# define ENABLE_FEATURE_SH_STANDALONE 0
63# define IF_FEATURE_SH_STANDALONE(...)
64# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
Eric Andersencb57d552001-06-28 07:25:16 +000065#endif
66
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000067#ifndef PIPE_BUF
Denis Vlasenko653d8e72009-03-19 21:59:35 +000068# define PIPE_BUF 4096 /* amount of buffering in a pipe */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000069#endif
70
Denys Vlasenko153fcaa2010-02-21 05:17:41 +010071#if !BB_MMU
Denis Vlasenko653d8e72009-03-19 21:59:35 +000072# error "Do not even bother, ash will not run on NOMMU machine"
Denis Vlasenkob012b102007-02-19 22:43:01 +000073#endif
74
Denys Vlasenkob9f2d9f2011-01-18 13:58:01 +010075//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
76//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
77//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
Denys Vlasenko771f1992010-07-16 14:31:34 +020078
Denys Vlasenkod383b492010-09-06 10:22:13 +020079//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko771f1992010-07-16 14:31:34 +020080//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
81
82//config:config ASH
83//config: bool "ash"
84//config: default y
85//config: depends on !NOMMU
86//config: help
87//config: Tha 'ash' shell adds about 60k in the default configuration and is
88//config: the most complete and most pedantically correct shell included with
89//config: busybox. This shell is actually a derivative of the Debian 'dash'
90//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
91//config: (written by Kenneth Almquist) from NetBSD.
92//config:
93//config:config ASH_BASH_COMPAT
94//config: bool "bash-compatible extensions"
95//config: default y
96//config: depends on ASH
97//config: help
98//config: Enable bash-compatible extensions.
99//config:
100//config:config ASH_JOB_CONTROL
101//config: bool "Job control"
102//config: default y
103//config: depends on ASH
104//config: help
105//config: Enable job control in the ash shell.
106//config:
107//config:config ASH_ALIAS
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100108//config: bool "Alias support"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200109//config: default y
110//config: depends on ASH
111//config: help
112//config: Enable alias support in the ash shell.
113//config:
114//config:config ASH_GETOPTS
115//config: bool "Builtin getopt to parse positional parameters"
116//config: default y
117//config: depends on ASH
118//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100119//config: Enable support for getopts builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200120//config:
121//config:config ASH_BUILTIN_ECHO
122//config: bool "Builtin version of 'echo'"
123//config: default y
124//config: depends on ASH
125//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100126//config: Enable support for echo builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200127//config:
128//config:config ASH_BUILTIN_PRINTF
129//config: bool "Builtin version of 'printf'"
130//config: default y
131//config: depends on ASH
132//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100133//config: Enable support for printf builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config:
135//config:config ASH_BUILTIN_TEST
136//config: bool "Builtin version of 'test'"
137//config: default y
138//config: depends on ASH
139//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100140//config: Enable support for test builtin in ash.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200141//config:
142//config:config ASH_CMDCMD
143//config: bool "'command' command to override shell builtins"
144//config: default y
145//config: depends on ASH
146//config: help
147//config: Enable support for the ash 'command' builtin, which allows
148//config: you to run the specified command with the specified arguments,
149//config: even when there is an ash builtin command with the same name.
150//config:
151//config:config ASH_MAIL
152//config: bool "Check for new mail on interactive shells"
153//config: default n
154//config: depends on ASH
155//config: help
Denys Vlasenko8c52f802011-02-04 17:36:21 +0100156//config: Enable "check for new mail" function in the ash shell.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200157//config:
158//config:config ASH_OPTIMIZE_FOR_SIZE
159//config: bool "Optimize for size instead of speed"
160//config: default y
161//config: depends on ASH
162//config: help
163//config: Compile ash for reduced size at the price of speed.
164//config:
165//config:config ASH_RANDOM_SUPPORT
166//config: bool "Pseudorandom generator and $RANDOM variable"
167//config: default y
168//config: depends on ASH
169//config: help
170//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
171//config: Each read of "$RANDOM" will generate a new pseudorandom value.
172//config: You can reset the generator by using a specified start value.
173//config: After "unset RANDOM" the generator will switch off and this
174//config: variable will no longer have special treatment.
175//config:
176//config:config ASH_EXPAND_PRMT
177//config: bool "Expand prompt string"
178//config: default y
179//config: depends on ASH
180//config: help
181//config: "PS#" may contain volatile content, such as backquote commands.
182//config: This option recreates the prompt string from the environment
183//config: variable each time it is displayed.
Denys Vlasenko51ca7762010-07-16 17:16:40 +0200184//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200185
186//usage:#define ash_trivial_usage NOUSAGE_STR
187//usage:#define ash_full_usage ""
188//usage:#define sh_trivial_usage NOUSAGE_STR
189//usage:#define sh_full_usage ""
190//usage:#define bash_trivial_usage NOUSAGE_STR
191//usage:#define bash_full_usage ""
192
Denis Vlasenkob012b102007-02-19 22:43:01 +0000193
Denis Vlasenko01631112007-12-16 17:20:38 +0000194/* ============ Hash table sizes. Configurable. */
195
196#define VTABSIZE 39
197#define ATABSIZE 39
198#define CMDTABLESIZE 31 /* should be prime */
199
200
Denis Vlasenkob012b102007-02-19 22:43:01 +0000201/* ============ Shell options */
202
203static const char *const optletters_optnames[] = {
204 "e" "errexit",
205 "f" "noglob",
206 "I" "ignoreeof",
207 "i" "interactive",
208 "m" "monitor",
209 "n" "noexec",
210 "s" "stdin",
211 "x" "xtrace",
212 "v" "verbose",
213 "C" "noclobber",
214 "a" "allexport",
215 "b" "notify",
216 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100217 "\0" "vi"
Michael Abbott359da5e2009-12-04 23:03:29 +0100218#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100219 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100220#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000221#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000222 ,"\0" "nolog"
223 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000224#endif
225};
226
Denys Vlasenko285ad152009-12-04 23:02:27 +0100227#define optletters(n) optletters_optnames[n][0]
228#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000229
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000230enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000231
Eric Andersenc470f442003-07-28 09:56:35 +0000232
Denis Vlasenkob012b102007-02-19 22:43:01 +0000233/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000234
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200235#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000236
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000237/*
Eric Andersenc470f442003-07-28 09:56:35 +0000238 * We enclose jmp_buf in a structure so that we can declare pointers to
239 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000240 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000241 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000242 * exception handlers, the user should save the value of handler on entry
243 * to an inner scope, set handler to point to a jmploc structure for the
244 * inner scope, and restore handler on exit from the scope.
245 */
Eric Andersenc470f442003-07-28 09:56:35 +0000246struct jmploc {
247 jmp_buf loc;
248};
Denis Vlasenko01631112007-12-16 17:20:38 +0000249
250struct globals_misc {
251 /* pid of main shell */
252 int rootpid;
253 /* shell level: 0 for the main shell, 1 for its children, and so on */
254 int shlvl;
255#define rootshell (!shlvl)
256 char *minusc; /* argument to -c option */
257
258 char *curdir; // = nullstr; /* current working directory */
259 char *physdir; // = nullstr; /* physical working directory */
260
261 char *arg0; /* value of $0 */
262
263 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000264
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200265 volatile int suppress_int; /* counter */
266 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000267 /* last pending signal */
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200268 volatile /*sig_atomic_t*/ smallint pending_sig;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000269 smallint exception_type; /* kind of exception (0..5) */
Denis Vlasenko01631112007-12-16 17:20:38 +0000270 /* exceptions */
Eric Andersenc470f442003-07-28 09:56:35 +0000271#define EXINT 0 /* SIGINT received */
272#define EXERROR 1 /* a generic error */
273#define EXSHELLPROC 2 /* execute a shell procedure */
274#define EXEXEC 3 /* command execution failed */
275#define EXEXIT 4 /* exit the shell */
276#define EXSIG 5 /* trapped signal in wait(1) */
Eric Andersen2870d962001-07-02 17:27:21 +0000277
Denis Vlasenko01631112007-12-16 17:20:38 +0000278 smallint isloginsh;
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000279 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000280
281 char optlist[NOPTS];
282#define eflag optlist[0]
283#define fflag optlist[1]
284#define Iflag optlist[2]
285#define iflag optlist[3]
286#define mflag optlist[4]
287#define nflag optlist[5]
288#define sflag optlist[6]
289#define xflag optlist[7]
290#define vflag optlist[8]
291#define Cflag optlist[9]
292#define aflag optlist[10]
293#define bflag optlist[11]
294#define uflag optlist[12]
295#define viflag optlist[13]
Michael Abbott359da5e2009-12-04 23:03:29 +0100296#if ENABLE_ASH_BASH_COMPAT
297# define pipefail optlist[14]
298#else
299# define pipefail 0
300#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000301#if DEBUG
Michael Abbott359da5e2009-12-04 23:03:29 +0100302# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
303# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000304#endif
305
306 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000307 /*
308 * Sigmode records the current value of the signal handlers for the various
309 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000310 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000311 */
312 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000313#define S_DFL 1 /* default signal handling (SIG_DFL) */
314#define S_CATCH 2 /* signal is caught */
315#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000316#define S_HARD_IGN 4 /* signal is ignored permenantly */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000317
Denis Vlasenko01631112007-12-16 17:20:38 +0000318 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000319 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200320 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000321 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200322 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000323
324 /* Rarely referenced stuff */
325#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200326 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000327#endif
328 pid_t backgndpid; /* pid of last background process */
329 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denis Vlasenko01631112007-12-16 17:20:38 +0000330};
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000331extern struct globals_misc *const ash_ptr_to_globals_misc;
332#define G_misc (*ash_ptr_to_globals_misc)
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000333#define rootpid (G_misc.rootpid )
334#define shlvl (G_misc.shlvl )
335#define minusc (G_misc.minusc )
336#define curdir (G_misc.curdir )
337#define physdir (G_misc.physdir )
338#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000339#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000340#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200341#define suppress_int (G_misc.suppress_int )
342#define pending_int (G_misc.pending_int )
343#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000344#define isloginsh (G_misc.isloginsh )
345#define nullstr (G_misc.nullstr )
346#define optlist (G_misc.optlist )
347#define sigmode (G_misc.sigmode )
348#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200349#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000350#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200351#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200352#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000353#define backgndpid (G_misc.backgndpid )
354#define job_warning (G_misc.job_warning)
Denis Vlasenko01631112007-12-16 17:20:38 +0000355#define INIT_G_misc() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000356 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
357 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +0000358 curdir = nullstr; \
359 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200360 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000361} while (0)
362
363
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000364/* ============ DEBUG */
365#if DEBUG
366static void trace_printf(const char *fmt, ...);
367static void trace_vprintf(const char *fmt, va_list va);
368# define TRACE(param) trace_printf param
369# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000370# define close(fd) do { \
371 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000372 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200373 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000374 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000375} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000376#else
377# define TRACE(param)
378# define TRACEV(param)
379#endif
380
381
Denis Vlasenko559691a2008-10-05 18:39:31 +0000382/* ============ Utility functions */
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000383#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
384
Denis Vlasenko559691a2008-10-05 18:39:31 +0000385static int isdigit_str9(const char *str)
386{
387 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
388 while (--maxlen && isdigit(*str))
389 str++;
390 return (*str == '\0');
391}
Denis Vlasenko01631112007-12-16 17:20:38 +0000392
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200393static const char *var_end(const char *var)
394{
395 while (*var)
396 if (*var++ == '=')
397 break;
398 return var;
399}
400
Denis Vlasenko559691a2008-10-05 18:39:31 +0000401
402/* ============ Interrupts / exceptions */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000403/*
Eric Andersen2870d962001-07-02 17:27:21 +0000404 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000405 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000406 * much more efficient and portable. (But hacking the kernel is so much
407 * more fun than worrying about efficiency and portability. :-))
408 */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000409#define INT_OFF do { \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200410 suppress_int++; \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000411 xbarrier(); \
412} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000413
414/*
415 * Called to raise an exception. Since C doesn't include exceptions, we
416 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000417 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000418 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000419static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000420static void
421raise_exception(int e)
422{
423#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000424 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000425 abort();
426#endif
427 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000428 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000429 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000430}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000431#if DEBUG
432#define raise_exception(e) do { \
433 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
434 raise_exception(e); \
435} while (0)
436#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000437
438/*
439 * Called from trap.c when a SIGINT is received. (If the user specifies
440 * that SIGINT is to be trapped or ignored using the trap builtin, then
441 * this routine is not called.) Suppressint is nonzero when interrupts
442 * are held using the INT_OFF macro. (The test for iflag is just
443 * defensive programming.)
444 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000445static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000446static void
447raise_interrupt(void)
448{
Denis Vlasenko4b875702009-03-19 13:30:04 +0000449 int ex_type;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000450
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200451 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000452 /* Signal is not automatically unmasked after it is raised,
453 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000454 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200455 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000456
Denis Vlasenko4b875702009-03-19 13:30:04 +0000457 ex_type = EXSIG;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000458 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
459 if (!(rootshell && iflag)) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000460 /* Kill ourself with SIGINT */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000461 signal(SIGINT, SIG_DFL);
462 raise(SIGINT);
463 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000464 ex_type = EXINT;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000465 }
Denis Vlasenko4b875702009-03-19 13:30:04 +0000466 raise_exception(ex_type);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000467 /* NOTREACHED */
468}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000469#if DEBUG
470#define raise_interrupt() do { \
471 TRACE(("raising interrupt on line %d\n", __LINE__)); \
472 raise_interrupt(); \
473} while (0)
474#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000475
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000476static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000477int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000478{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000479 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200480 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000481 raise_interrupt();
482 }
483}
484#define INT_ON int_on()
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000485static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000486force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000487{
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +0000488 xbarrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200489 suppress_int = 0;
490 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000491 raise_interrupt();
492}
493#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000494
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200495#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000496
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000497#define RESTORE_INT(v) do { \
498 xbarrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200499 suppress_int = (v); \
500 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000501 raise_interrupt(); \
502} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000503
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000504
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000505/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000506
Eric Andersenc470f442003-07-28 09:56:35 +0000507static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000508outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000509{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000510 INT_OFF;
511 fputs(p, file);
512 INT_ON;
513}
514
515static void
516flush_stdout_stderr(void)
517{
518 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100519 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000520 INT_ON;
521}
522
523static void
524outcslow(int c, FILE *dest)
525{
526 INT_OFF;
527 putc(c, dest);
528 fflush(dest);
529 INT_ON;
530}
531
532static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
533static int
534out1fmt(const char *fmt, ...)
535{
536 va_list ap;
537 int r;
538
539 INT_OFF;
540 va_start(ap, fmt);
541 r = vprintf(fmt, ap);
542 va_end(ap);
543 INT_ON;
544 return r;
545}
546
547static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
548static int
549fmtstr(char *outbuf, size_t length, const char *fmt, ...)
550{
551 va_list ap;
552 int ret;
553
554 va_start(ap, fmt);
555 INT_OFF;
556 ret = vsnprintf(outbuf, length, fmt, ap);
557 va_end(ap);
558 INT_ON;
559 return ret;
560}
561
562static void
563out1str(const char *p)
564{
565 outstr(p, stdout);
566}
567
568static void
569out2str(const char *p)
570{
571 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100572 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000573}
574
575
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000576/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000577
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000578/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100579#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200580#define CTLESC ((unsigned char)'\201') /* escape next character */
581#define CTLVAR ((unsigned char)'\202') /* variable defn */
582#define CTLENDVAR ((unsigned char)'\203')
583#define CTLBACKQ ((unsigned char)'\204')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000584#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
585/* CTLBACKQ | CTLQUOTE == '\205' */
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200586#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
587#define CTLENDARI ((unsigned char)'\207')
588#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100589#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000590
591/* variable substitution byte (follows CTLVAR) */
592#define VSTYPE 0x0f /* type of variable substitution */
593#define VSNUL 0x10 /* colon--treat the empty string as unset */
594#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
595
596/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000597#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
598#define VSMINUS 0x2 /* ${var-text} */
599#define VSPLUS 0x3 /* ${var+text} */
600#define VSQUESTION 0x4 /* ${var?message} */
601#define VSASSIGN 0x5 /* ${var=text} */
602#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
603#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
604#define VSTRIMLEFT 0x8 /* ${var#pattern} */
605#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
606#define VSLENGTH 0xa /* ${#var} */
607#if ENABLE_ASH_BASH_COMPAT
608#define VSSUBSTR 0xc /* ${var:position:length} */
609#define VSREPLACE 0xd /* ${var/pattern/replacement} */
610#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
611#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000612
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000613static const char dolatstr[] ALIGN1 = {
614 CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
615};
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000616
Denis Vlasenko559691a2008-10-05 18:39:31 +0000617#define NCMD 0
618#define NPIPE 1
619#define NREDIR 2
620#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000621#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000622#define NAND 5
623#define NOR 6
624#define NSEMI 7
625#define NIF 8
626#define NWHILE 9
627#define NUNTIL 10
628#define NFOR 11
629#define NCASE 12
630#define NCLIST 13
631#define NDEFUN 14
632#define NARG 15
633#define NTO 16
634#if ENABLE_ASH_BASH_COMPAT
635#define NTO2 17
636#endif
637#define NCLOBBER 18
638#define NFROM 19
639#define NFROMTO 20
640#define NAPPEND 21
641#define NTOFD 22
642#define NFROMFD 23
643#define NHERE 24
644#define NXHERE 25
645#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000646#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000647
648union node;
649
650struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000651 smallint type; /* Nxxxx */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000652 union node *assign;
653 union node *args;
654 union node *redirect;
655};
656
657struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000658 smallint type;
659 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000660 struct nodelist *cmdlist;
661};
662
663struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000664 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000665 union node *n;
666 union node *redirect;
667};
668
669struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000670 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000671 union node *ch1;
672 union node *ch2;
673};
674
675struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000676 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000677 union node *test;
678 union node *ifpart;
679 union node *elsepart;
680};
681
682struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000683 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000684 union node *args;
685 union node *body;
686 char *var;
687};
688
689struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000690 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000691 union node *expr;
692 union node *cases;
693};
694
695struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000696 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000697 union node *next;
698 union node *pattern;
699 union node *body;
700};
701
702struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000703 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000704 union node *next;
705 char *text;
706 struct nodelist *backquote;
707};
708
Denis Vlasenko559691a2008-10-05 18:39:31 +0000709/* nfile and ndup layout must match!
710 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
711 * that it is actually NTO2 (>&file), and change its type.
712 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000713struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000714 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000715 union node *next;
716 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000717 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718 union node *fname;
719 char *expfname;
720};
721
722struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000723 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000724 union node *next;
725 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000726 int dupfd;
727 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000728 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000729};
730
731struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000732 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000733 union node *next;
734 int fd;
735 union node *doc;
736};
737
738struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000739 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000740 union node *com;
741};
742
743union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000744 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000745 struct ncmd ncmd;
746 struct npipe npipe;
747 struct nredir nredir;
748 struct nbinary nbinary;
749 struct nif nif;
750 struct nfor nfor;
751 struct ncase ncase;
752 struct nclist nclist;
753 struct narg narg;
754 struct nfile nfile;
755 struct ndup ndup;
756 struct nhere nhere;
757 struct nnot nnot;
758};
759
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200760/*
761 * NODE_EOF is returned by parsecmd when it encounters an end of file.
762 * It must be distinct from NULL.
763 */
764#define NODE_EOF ((union node *) -1L)
765
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000766struct nodelist {
767 struct nodelist *next;
768 union node *n;
769};
770
771struct funcnode {
772 int count;
773 union node n;
774};
775
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000776/*
777 * Free a parse tree.
778 */
779static void
780freefunc(struct funcnode *f)
781{
782 if (f && --f->count < 0)
783 free(f);
784}
785
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000786
787/* ============ Debugging output */
788
789#if DEBUG
790
791static FILE *tracefile;
792
793static void
794trace_printf(const char *fmt, ...)
795{
796 va_list va;
797
798 if (debug != 1)
799 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000800 if (DEBUG_TIME)
801 fprintf(tracefile, "%u ", (int) time(NULL));
802 if (DEBUG_PID)
803 fprintf(tracefile, "[%u] ", (int) getpid());
804 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200805 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000806 va_start(va, fmt);
807 vfprintf(tracefile, fmt, va);
808 va_end(va);
809}
810
811static void
812trace_vprintf(const char *fmt, va_list va)
813{
814 if (debug != 1)
815 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000816 if (DEBUG_TIME)
817 fprintf(tracefile, "%u ", (int) time(NULL));
818 if (DEBUG_PID)
819 fprintf(tracefile, "[%u] ", (int) getpid());
820 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200821 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000822 vfprintf(tracefile, fmt, va);
823}
824
825static void
826trace_puts(const char *s)
827{
828 if (debug != 1)
829 return;
830 fputs(s, tracefile);
831}
832
833static void
834trace_puts_quoted(char *s)
835{
836 char *p;
837 char c;
838
839 if (debug != 1)
840 return;
841 putc('"', tracefile);
842 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100843 switch ((unsigned char)*p) {
844 case '\n': c = 'n'; goto backslash;
845 case '\t': c = 't'; goto backslash;
846 case '\r': c = 'r'; goto backslash;
847 case '\"': c = '\"'; goto backslash;
848 case '\\': c = '\\'; goto backslash;
849 case CTLESC: c = 'e'; goto backslash;
850 case CTLVAR: c = 'v'; goto backslash;
851 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
852 case CTLBACKQ: c = 'q'; goto backslash;
853 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000854 backslash:
855 putc('\\', tracefile);
856 putc(c, tracefile);
857 break;
858 default:
859 if (*p >= ' ' && *p <= '~')
860 putc(*p, tracefile);
861 else {
862 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +0100863 putc((*p >> 6) & 03, tracefile);
864 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000865 putc(*p & 07, tracefile);
866 }
867 break;
868 }
869 }
870 putc('"', tracefile);
871}
872
873static void
874trace_puts_args(char **ap)
875{
876 if (debug != 1)
877 return;
878 if (!*ap)
879 return;
880 while (1) {
881 trace_puts_quoted(*ap);
882 if (!*++ap) {
883 putc('\n', tracefile);
884 break;
885 }
886 putc(' ', tracefile);
887 }
888}
889
890static void
891opentrace(void)
892{
893 char s[100];
894#ifdef O_APPEND
895 int flags;
896#endif
897
898 if (debug != 1) {
899 if (tracefile)
900 fflush(tracefile);
901 /* leave open because libedit might be using it */
902 return;
903 }
904 strcpy(s, "./trace");
905 if (tracefile) {
906 if (!freopen(s, "a", tracefile)) {
907 fprintf(stderr, "Can't re-open %s\n", s);
908 debug = 0;
909 return;
910 }
911 } else {
912 tracefile = fopen(s, "a");
913 if (tracefile == NULL) {
914 fprintf(stderr, "Can't open %s\n", s);
915 debug = 0;
916 return;
917 }
918 }
919#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +0000920 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000921 if (flags >= 0)
922 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
923#endif
924 setlinebuf(tracefile);
925 fputs("\nTracing started.\n", tracefile);
926}
927
928static void
929indent(int amount, char *pfx, FILE *fp)
930{
931 int i;
932
933 for (i = 0; i < amount; i++) {
934 if (pfx && i == amount - 1)
935 fputs(pfx, fp);
936 putc('\t', fp);
937 }
938}
939
940/* little circular references here... */
941static void shtree(union node *n, int ind, char *pfx, FILE *fp);
942
943static void
944sharg(union node *arg, FILE *fp)
945{
946 char *p;
947 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +0100948 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000949
950 if (arg->type != NARG) {
951 out1fmt("<node type %d>\n", arg->type);
952 abort();
953 }
954 bqlist = arg->narg.backquote;
955 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +0100956 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000957 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -0700958 p++;
959 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000960 break;
961 case CTLVAR:
962 putc('$', fp);
963 putc('{', fp);
964 subtype = *++p;
965 if (subtype == VSLENGTH)
966 putc('#', fp);
967
Dan Fandrich77d48722010-09-07 23:38:28 -0700968 while (*p != '=') {
969 putc(*p, fp);
970 p++;
971 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000972
973 if (subtype & VSNUL)
974 putc(':', fp);
975
976 switch (subtype & VSTYPE) {
977 case VSNORMAL:
978 putc('}', fp);
979 break;
980 case VSMINUS:
981 putc('-', fp);
982 break;
983 case VSPLUS:
984 putc('+', fp);
985 break;
986 case VSQUESTION:
987 putc('?', fp);
988 break;
989 case VSASSIGN:
990 putc('=', fp);
991 break;
992 case VSTRIMLEFT:
993 putc('#', fp);
994 break;
995 case VSTRIMLEFTMAX:
996 putc('#', fp);
997 putc('#', fp);
998 break;
999 case VSTRIMRIGHT:
1000 putc('%', fp);
1001 break;
1002 case VSTRIMRIGHTMAX:
1003 putc('%', fp);
1004 putc('%', fp);
1005 break;
1006 case VSLENGTH:
1007 break;
1008 default:
1009 out1fmt("<subtype %d>", subtype);
1010 }
1011 break;
1012 case CTLENDVAR:
1013 putc('}', fp);
1014 break;
1015 case CTLBACKQ:
1016 case CTLBACKQ|CTLQUOTE:
1017 putc('$', fp);
1018 putc('(', fp);
1019 shtree(bqlist->n, -1, NULL, fp);
1020 putc(')', fp);
1021 break;
1022 default:
1023 putc(*p, fp);
1024 break;
1025 }
1026 }
1027}
1028
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001029static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001030shcmd(union node *cmd, FILE *fp)
1031{
1032 union node *np;
1033 int first;
1034 const char *s;
1035 int dftfd;
1036
1037 first = 1;
1038 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001039 if (!first)
1040 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001041 sharg(np, fp);
1042 first = 0;
1043 }
1044 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001045 if (!first)
1046 putc(' ', fp);
1047 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001048 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001049 case NTO: s = ">>"+1; dftfd = 1; break;
1050 case NCLOBBER: s = ">|"; dftfd = 1; break;
1051 case NAPPEND: s = ">>"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001052#if ENABLE_ASH_BASH_COMPAT
1053 case NTO2:
1054#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001055 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001056 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001057 case NFROMFD: s = "<&"; break;
1058 case NFROMTO: s = "<>"; break;
1059 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001060 }
1061 if (np->nfile.fd != dftfd)
1062 fprintf(fp, "%d", np->nfile.fd);
1063 fputs(s, fp);
1064 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1065 fprintf(fp, "%d", np->ndup.dupfd);
1066 } else {
1067 sharg(np->nfile.fname, fp);
1068 }
1069 first = 0;
1070 }
1071}
1072
1073static void
1074shtree(union node *n, int ind, char *pfx, FILE *fp)
1075{
1076 struct nodelist *lp;
1077 const char *s;
1078
1079 if (n == NULL)
1080 return;
1081
1082 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001083
1084 if (n == NODE_EOF) {
1085 fputs("<EOF>", fp);
1086 return;
1087 }
1088
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001089 switch (n->type) {
1090 case NSEMI:
1091 s = "; ";
1092 goto binop;
1093 case NAND:
1094 s = " && ";
1095 goto binop;
1096 case NOR:
1097 s = " || ";
1098 binop:
1099 shtree(n->nbinary.ch1, ind, NULL, fp);
1100 /* if (ind < 0) */
1101 fputs(s, fp);
1102 shtree(n->nbinary.ch2, ind, NULL, fp);
1103 break;
1104 case NCMD:
1105 shcmd(n, fp);
1106 if (ind >= 0)
1107 putc('\n', fp);
1108 break;
1109 case NPIPE:
1110 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001111 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001112 if (lp->next)
1113 fputs(" | ", fp);
1114 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001115 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001116 fputs(" &", fp);
1117 if (ind >= 0)
1118 putc('\n', fp);
1119 break;
1120 default:
1121 fprintf(fp, "<node type %d>", n->type);
1122 if (ind >= 0)
1123 putc('\n', fp);
1124 break;
1125 }
1126}
1127
1128static void
1129showtree(union node *n)
1130{
1131 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001132 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001133}
1134
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001135#endif /* DEBUG */
1136
1137
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001138/* ============ Parser data */
1139
1140/*
Denis Vlasenkob012b102007-02-19 22:43:01 +00001141 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
1142 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001143struct strlist {
1144 struct strlist *next;
1145 char *text;
1146};
1147
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001148struct alias;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001149
Denis Vlasenkob012b102007-02-19 22:43:01 +00001150struct strpush {
1151 struct strpush *prev; /* preceding string on stack */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001152 char *prev_string;
1153 int prev_left_in_line;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001154#if ENABLE_ASH_ALIAS
1155 struct alias *ap; /* if push was associated with an alias */
1156#endif
1157 char *string; /* remember the string since it may change */
1158};
1159
1160struct parsefile {
1161 struct parsefile *prev; /* preceding file on stack */
1162 int linno; /* current line */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001163 int pf_fd; /* file descriptor (or -1 if string) */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00001164 int left_in_line; /* number of chars left in this line */
1165 int left_in_buffer; /* number of chars left in this buffer past the line */
1166 char *next_to_pgetc; /* next char in buffer */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001167 char *buf; /* input buffer */
1168 struct strpush *strpush; /* for pushing strings at this level */
1169 struct strpush basestrpush; /* so pushing one is fast */
1170};
1171
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001172static struct parsefile basepf; /* top level input file */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00001173static struct parsefile *g_parsefile = &basepf; /* current input file */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001174static int startlinno; /* line # where last token started */
1175static char *commandname; /* currently executing command */
1176static struct strlist *cmdenviron; /* environment for builtin command */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001177static uint8_t exitstatus; /* exit status of last command */
Denis Vlasenkob012b102007-02-19 22:43:01 +00001178
1179
1180/* ============ Message printing */
1181
1182static void
1183ash_vmsg(const char *msg, va_list ap)
1184{
1185 fprintf(stderr, "%s: ", arg0);
1186 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001187 if (strcmp(arg0, commandname))
1188 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001189 if (!iflag || g_parsefile->pf_fd > 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001190 fprintf(stderr, "line %d: ", startlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001191 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001192 vfprintf(stderr, msg, ap);
1193 outcslow('\n', stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001194}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001195
1196/*
1197 * Exverror is called to raise the error exception. If the second argument
1198 * is not NULL then error prints an error message using printf style
1199 * formatting. It then raises the error exception.
1200 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001201static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001202static void
1203ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001204{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001205#if DEBUG
1206 if (msg) {
1207 TRACE(("ash_vmsg_and_raise(%d, \"", cond));
1208 TRACEV((msg, ap));
1209 TRACE(("\") pid=%d\n", getpid()));
1210 } else
1211 TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
1212 if (msg)
1213#endif
1214 ash_vmsg(msg, ap);
1215
1216 flush_stdout_stderr();
1217 raise_exception(cond);
1218 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001219}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001220
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001221static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001222static void
1223ash_msg_and_raise_error(const char *msg, ...)
1224{
1225 va_list ap;
1226
1227 va_start(ap, msg);
1228 ash_vmsg_and_raise(EXERROR, msg, ap);
1229 /* NOTREACHED */
1230 va_end(ap);
1231}
1232
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001233static void raise_error_syntax(const char *) NORETURN;
1234static void
1235raise_error_syntax(const char *msg)
1236{
1237 ash_msg_and_raise_error("syntax error: %s", msg);
1238 /* NOTREACHED */
1239}
1240
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001241static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001242static void
1243ash_msg_and_raise(int cond, const char *msg, ...)
1244{
1245 va_list ap;
1246
1247 va_start(ap, msg);
1248 ash_vmsg_and_raise(cond, msg, ap);
1249 /* NOTREACHED */
1250 va_end(ap);
1251}
1252
1253/*
1254 * error/warning routines for external builtins
1255 */
1256static void
1257ash_msg(const char *fmt, ...)
1258{
1259 va_list ap;
1260
1261 va_start(ap, fmt);
1262 ash_vmsg(fmt, ap);
1263 va_end(ap);
1264}
1265
1266/*
1267 * Return a string describing an error. The returned string may be a
1268 * pointer to a static buffer that will be overwritten on the next call.
1269 * Action describes the operation that got the error.
1270 */
1271static const char *
1272errmsg(int e, const char *em)
1273{
1274 if (e == ENOENT || e == ENOTDIR) {
1275 return em;
1276 }
1277 return strerror(e);
1278}
1279
1280
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001281/* ============ Memory allocation */
1282
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001283#if 0
1284/* I consider these wrappers nearly useless:
1285 * ok, they return you to nearest exception handler, but
1286 * how much memory do you leak in the process, making
1287 * memory starvation worse?
1288 */
1289static void *
1290ckrealloc(void * p, size_t nbytes)
1291{
1292 p = realloc(p, nbytes);
1293 if (!p)
1294 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1295 return p;
1296}
1297
1298static void *
1299ckmalloc(size_t nbytes)
1300{
1301 return ckrealloc(NULL, nbytes);
1302}
1303
1304static void *
1305ckzalloc(size_t nbytes)
1306{
1307 return memset(ckmalloc(nbytes), 0, nbytes);
1308}
1309
1310static char *
1311ckstrdup(const char *s)
1312{
1313 char *p = strdup(s);
1314 if (!p)
1315 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1316 return p;
1317}
1318#else
1319/* Using bbox equivalents. They exit if out of memory */
1320# define ckrealloc xrealloc
1321# define ckmalloc xmalloc
1322# define ckzalloc xzalloc
1323# define ckstrdup xstrdup
1324#endif
1325
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001326/*
1327 * It appears that grabstackstr() will barf with such alignments
1328 * because stalloc() will return a string allocated in a new stackblock.
1329 */
1330#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1331enum {
1332 /* Most machines require the value returned from malloc to be aligned
1333 * in some way. The following macro will get this right
1334 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001335 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001336 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001337 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001338};
1339
1340struct stack_block {
1341 struct stack_block *prev;
1342 char space[MINSIZE];
1343};
1344
1345struct stackmark {
1346 struct stack_block *stackp;
1347 char *stacknxt;
1348 size_t stacknleft;
1349 struct stackmark *marknext;
1350};
1351
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001352
Denis Vlasenko01631112007-12-16 17:20:38 +00001353struct globals_memstack {
1354 struct stack_block *g_stackp; // = &stackbase;
1355 struct stackmark *markp;
1356 char *g_stacknxt; // = stackbase.space;
1357 char *sstrend; // = stackbase.space + MINSIZE;
1358 size_t g_stacknleft; // = MINSIZE;
1359 int herefd; // = -1;
1360 struct stack_block stackbase;
1361};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001362extern struct globals_memstack *const ash_ptr_to_globals_memstack;
1363#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001364#define g_stackp (G_memstack.g_stackp )
1365#define markp (G_memstack.markp )
1366#define g_stacknxt (G_memstack.g_stacknxt )
1367#define sstrend (G_memstack.sstrend )
1368#define g_stacknleft (G_memstack.g_stacknleft)
1369#define herefd (G_memstack.herefd )
1370#define stackbase (G_memstack.stackbase )
1371#define INIT_G_memstack() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001372 (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
1373 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001374 g_stackp = &stackbase; \
1375 g_stacknxt = stackbase.space; \
1376 g_stacknleft = MINSIZE; \
1377 sstrend = stackbase.space + MINSIZE; \
1378 herefd = -1; \
1379} while (0)
1380
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001381
Denis Vlasenko01631112007-12-16 17:20:38 +00001382#define stackblock() ((void *)g_stacknxt)
1383#define stackblocksize() g_stacknleft
1384
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001385/*
1386 * Parse trees for commands are allocated in lifo order, so we use a stack
1387 * to make this more efficient, and also to avoid all sorts of exception
1388 * handling code to handle interrupts in the middle of a parse.
1389 *
1390 * The size 504 was chosen because the Ultrix malloc handles that size
1391 * well.
1392 */
1393static void *
1394stalloc(size_t nbytes)
1395{
1396 char *p;
1397 size_t aligned;
1398
1399 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001400 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001401 size_t len;
1402 size_t blocksize;
1403 struct stack_block *sp;
1404
1405 blocksize = aligned;
1406 if (blocksize < MINSIZE)
1407 blocksize = MINSIZE;
1408 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1409 if (len < blocksize)
1410 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1411 INT_OFF;
1412 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001413 sp->prev = g_stackp;
1414 g_stacknxt = sp->space;
1415 g_stacknleft = blocksize;
1416 sstrend = g_stacknxt + blocksize;
1417 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001418 INT_ON;
1419 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001420 p = g_stacknxt;
1421 g_stacknxt += aligned;
1422 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001423 return p;
1424}
1425
Denis Vlasenko597906c2008-02-20 16:38:54 +00001426static void *
1427stzalloc(size_t nbytes)
1428{
1429 return memset(stalloc(nbytes), 0, nbytes);
1430}
1431
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001432static void
1433stunalloc(void *p)
1434{
1435#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001436 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001437 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001438 abort();
1439 }
1440#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001441 g_stacknleft += g_stacknxt - (char *)p;
1442 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001443}
1444
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001445/*
1446 * Like strdup but works with the ash stack.
1447 */
1448static char *
1449ststrdup(const char *p)
1450{
1451 size_t len = strlen(p) + 1;
1452 return memcpy(stalloc(len), p, len);
1453}
1454
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001455static void
1456setstackmark(struct stackmark *mark)
1457{
Denis Vlasenko01631112007-12-16 17:20:38 +00001458 mark->stackp = g_stackp;
1459 mark->stacknxt = g_stacknxt;
1460 mark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001461 mark->marknext = markp;
1462 markp = mark;
1463}
1464
1465static void
1466popstackmark(struct stackmark *mark)
1467{
1468 struct stack_block *sp;
1469
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001470 if (!mark->stackp)
1471 return;
1472
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001473 INT_OFF;
1474 markp = mark->marknext;
Denis Vlasenko01631112007-12-16 17:20:38 +00001475 while (g_stackp != mark->stackp) {
1476 sp = g_stackp;
1477 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001478 free(sp);
1479 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001480 g_stacknxt = mark->stacknxt;
1481 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001482 sstrend = mark->stacknxt + mark->stacknleft;
1483 INT_ON;
1484}
1485
1486/*
1487 * When the parser reads in a string, it wants to stick the string on the
1488 * stack and only adjust the stack pointer when it knows how big the
1489 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1490 * of space on top of the stack and stackblocklen returns the length of
1491 * this block. Growstackblock will grow this space by at least one byte,
1492 * possibly moving it (like realloc). Grabstackblock actually allocates the
1493 * part of the block that has been used.
1494 */
1495static void
1496growstackblock(void)
1497{
1498 size_t newlen;
1499
Denis Vlasenko01631112007-12-16 17:20:38 +00001500 newlen = g_stacknleft * 2;
1501 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001502 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1503 if (newlen < 128)
1504 newlen += 128;
1505
Denis Vlasenko01631112007-12-16 17:20:38 +00001506 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001507 struct stack_block *oldstackp;
1508 struct stackmark *xmark;
1509 struct stack_block *sp;
1510 struct stack_block *prevstackp;
1511 size_t grosslen;
1512
1513 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001514 oldstackp = g_stackp;
1515 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001516 prevstackp = sp->prev;
1517 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1518 sp = ckrealloc(sp, grosslen);
1519 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001520 g_stackp = sp;
1521 g_stacknxt = sp->space;
1522 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001523 sstrend = sp->space + newlen;
1524
1525 /*
1526 * Stack marks pointing to the start of the old block
1527 * must be relocated to point to the new block
1528 */
1529 xmark = markp;
1530 while (xmark != NULL && xmark->stackp == oldstackp) {
Denis Vlasenko01631112007-12-16 17:20:38 +00001531 xmark->stackp = g_stackp;
1532 xmark->stacknxt = g_stacknxt;
1533 xmark->stacknleft = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001534 xmark = xmark->marknext;
1535 }
1536 INT_ON;
1537 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001538 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001539 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001540 char *p = stalloc(newlen);
1541
1542 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001543 g_stacknxt = memcpy(p, oldspace, oldlen);
1544 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001545 }
1546}
1547
1548static void
1549grabstackblock(size_t len)
1550{
1551 len = SHELL_ALIGN(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001552 g_stacknxt += len;
1553 g_stacknleft -= len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001554}
1555
1556/*
1557 * The following routines are somewhat easier to use than the above.
1558 * The user declares a variable of type STACKSTR, which may be declared
1559 * to be a register. The macro STARTSTACKSTR initializes things. Then
1560 * the user uses the macro STPUTC to add characters to the string. In
1561 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1562 * grown as necessary. When the user is done, she can just leave the
1563 * string there and refer to it using stackblock(). Or she can allocate
1564 * the space for it using grabstackstr(). If it is necessary to allow
1565 * someone else to use the stack temporarily and then continue to grow
1566 * the string, the user should use grabstack to allocate the space, and
1567 * then call ungrabstr(p) to return to the previous mode of operation.
1568 *
1569 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1570 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1571 * is space for at least one character.
1572 */
1573static void *
1574growstackstr(void)
1575{
1576 size_t len = stackblocksize();
1577 if (herefd >= 0 && len >= 1024) {
1578 full_write(herefd, stackblock(), len);
1579 return stackblock();
1580 }
1581 growstackblock();
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001582 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001583}
1584
1585/*
1586 * Called from CHECKSTRSPACE.
1587 */
1588static char *
1589makestrspace(size_t newlen, char *p)
1590{
Denis Vlasenko01631112007-12-16 17:20:38 +00001591 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001592 size_t size = stackblocksize();
1593
1594 for (;;) {
1595 size_t nleft;
1596
1597 size = stackblocksize();
1598 nleft = size - len;
1599 if (nleft >= newlen)
1600 break;
1601 growstackblock();
1602 }
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001603 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001604}
1605
1606static char *
1607stack_nputstr(const char *s, size_t n, char *p)
1608{
1609 p = makestrspace(n, p);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001610 p = (char *)memcpy(p, s, n) + n;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001611 return p;
1612}
1613
1614static char *
1615stack_putstr(const char *s, char *p)
1616{
1617 return stack_nputstr(s, strlen(s), p);
1618}
1619
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001620static char *
1621_STPUTC(int c, char *p)
1622{
1623 if (p == sstrend)
1624 p = growstackstr();
1625 *p++ = c;
1626 return p;
1627}
1628
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001629#define STARTSTACKSTR(p) ((p) = stackblock())
1630#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001631#define CHECKSTRSPACE(n, p) do { \
1632 char *q = (p); \
1633 size_t l = (n); \
1634 size_t m = sstrend - q; \
1635 if (l > m) \
1636 (p) = makestrspace(l, q); \
1637} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001638#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001639#define STACKSTRNUL(p) do { \
1640 if ((p) == sstrend) \
1641 (p) = growstackstr(); \
1642 *(p) = '\0'; \
1643} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001644#define STUNPUTC(p) (--(p))
1645#define STTOPC(p) ((p)[-1])
1646#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001647
1648#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001649#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001650#define stackstrend() ((void *)sstrend)
1651
1652
1653/* ============ String helpers */
1654
1655/*
1656 * prefix -- see if pfx is a prefix of string.
1657 */
1658static char *
1659prefix(const char *string, const char *pfx)
1660{
1661 while (*pfx) {
1662 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001663 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001664 }
1665 return (char *) string;
1666}
1667
1668/*
1669 * Check for a valid number. This should be elsewhere.
1670 */
1671static int
1672is_number(const char *p)
1673{
1674 do {
1675 if (!isdigit(*p))
1676 return 0;
1677 } while (*++p != '\0');
1678 return 1;
1679}
1680
1681/*
1682 * Convert a string of digits to an integer, printing an error message on
1683 * failure.
1684 */
1685static int
1686number(const char *s)
1687{
1688 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001689 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001690 return atoi(s);
1691}
1692
1693/*
1694 * Produce a possibly single quoted string suitable as input to the shell.
1695 * The return string is allocated on the stack.
1696 */
1697static char *
1698single_quote(const char *s)
1699{
1700 char *p;
1701
1702 STARTSTACKSTR(p);
1703
1704 do {
1705 char *q;
1706 size_t len;
1707
1708 len = strchrnul(s, '\'') - s;
1709
1710 q = p = makestrspace(len + 3, p);
1711
1712 *q++ = '\'';
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001713 q = (char *)memcpy(q, s, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001714 *q++ = '\'';
1715 s += len;
1716
1717 STADJUST(q - p, p);
1718
Denys Vlasenkocd716832009-11-28 22:14:02 +01001719 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001720 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001721 len = 0;
1722 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001723
1724 q = p = makestrspace(len + 3, p);
1725
1726 *q++ = '"';
Denys Vlasenkocd716832009-11-28 22:14:02 +01001727 q = (char *)memcpy(q, s - len, len) + len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001728 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001729
1730 STADJUST(q - p, p);
1731 } while (*s);
1732
Denys Vlasenkocd716832009-11-28 22:14:02 +01001733 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001734
1735 return stackblock();
1736}
1737
1738
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001739/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001740
1741static char **argptr; /* argument list for builtin commands */
1742static char *optionarg; /* set by nextopt (like getopt) */
1743static char *optptr; /* used by nextopt */
1744
1745/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001746 * XXX - should get rid of. Have all builtins use getopt(3).
1747 * The library getopt must have the BSD extension static variable
1748 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001749 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001750 * Standard option processing (a la getopt) for builtin routines.
1751 * The only argument that is passed to nextopt is the option string;
1752 * the other arguments are unnecessary. It returns the character,
1753 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001754 */
1755static int
1756nextopt(const char *optstring)
1757{
1758 char *p;
1759 const char *q;
1760 char c;
1761
1762 p = optptr;
1763 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001764 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001765 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001766 if (p == NULL)
1767 return '\0';
1768 if (*p != '-')
1769 return '\0';
1770 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001771 return '\0';
1772 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001773 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001774 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001775 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001776 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001777 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001778 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001779 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001780 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001781 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001782 if (*++q == ':')
1783 q++;
1784 }
1785 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001786 if (*p == '\0') {
1787 p = *argptr++;
1788 if (p == NULL)
1789 ash_msg_and_raise_error("no arg for -%c option", c);
1790 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001791 optionarg = p;
1792 p = NULL;
1793 }
1794 optptr = p;
1795 return c;
1796}
1797
1798
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001799/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001800
Denis Vlasenko01631112007-12-16 17:20:38 +00001801/*
1802 * The parsefile structure pointed to by the global variable parsefile
1803 * contains information about the current file being read.
1804 */
Denis Vlasenko01631112007-12-16 17:20:38 +00001805struct shparam {
1806 int nparam; /* # of positional parameters (without $0) */
1807#if ENABLE_ASH_GETOPTS
1808 int optind; /* next parameter to be processed by getopts */
1809 int optoff; /* used by getopts */
1810#endif
1811 unsigned char malloced; /* if parameter list dynamically allocated */
1812 char **p; /* parameter list */
1813};
1814
1815/*
1816 * Free the list of positional parameters.
1817 */
1818static void
1819freeparam(volatile struct shparam *param)
1820{
Denis Vlasenko01631112007-12-16 17:20:38 +00001821 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00001822 char **ap, **ap1;
1823 ap = ap1 = param->p;
1824 while (*ap)
1825 free(*ap++);
1826 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00001827 }
1828}
1829
1830#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001831static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00001832#endif
1833
1834struct var {
1835 struct var *next; /* next entry in hash list */
1836 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001837 const char *var_text; /* name=value */
1838 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00001839 /* the variable gets set/unset */
1840};
1841
1842struct localvar {
1843 struct localvar *next; /* next local variable in list */
1844 struct var *vp; /* the variable that was made local */
1845 int flags; /* saved flags */
1846 const char *text; /* saved text */
1847};
1848
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001849/* flags */
1850#define VEXPORT 0x01 /* variable is exported */
1851#define VREADONLY 0x02 /* variable cannot be modified */
1852#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1853#define VTEXTFIXED 0x08 /* text is statically allocated */
1854#define VSTACK 0x10 /* text is allocated on the stack */
1855#define VUNSET 0x20 /* the variable is not set */
1856#define VNOFUNC 0x40 /* don't call the callback function */
1857#define VNOSET 0x80 /* do not set variable - just readonly test */
1858#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00001859#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001860# define VDYNAMIC 0x200 /* dynamic variable */
1861#else
1862# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001863#endif
1864
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001865
Denis Vlasenko01631112007-12-16 17:20:38 +00001866/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001867#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001868static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001869change_lc_all(const char *value)
1870{
1871 if (value && *value != '\0')
1872 setlocale(LC_ALL, value);
1873}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02001874static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00001875change_lc_ctype(const char *value)
1876{
1877 if (value && *value != '\0')
1878 setlocale(LC_CTYPE, value);
1879}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00001880#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001881#if ENABLE_ASH_MAIL
1882static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01001883static void changemail(const char *var_value) FAST_FUNC;
1884#else
1885# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001886#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001887static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001888#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001889static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001890#endif
1891
Denis Vlasenko01631112007-12-16 17:20:38 +00001892static const struct {
1893 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001894 const char *var_text;
1895 void (*var_func)(const char *) FAST_FUNC;
Denis Vlasenko01631112007-12-16 17:20:38 +00001896} varinit_data[] = {
Denis Vlasenko01631112007-12-16 17:20:38 +00001897 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001898#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001899 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
1900 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001901#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001902 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
1903 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
1904 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
1905 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906#if ENABLE_ASH_GETOPTS
Denis Vlasenko01631112007-12-16 17:20:38 +00001907 { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001908#endif
1909#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001910 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911#endif
1912#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001913 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
1914 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001915#endif
1916#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001917 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001918#endif
1919};
1920
Denis Vlasenko0b769642008-07-24 07:54:57 +00001921struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00001922
1923struct globals_var {
1924 struct shparam shellparam; /* $@ current positional parameters */
1925 struct redirtab *redirlist;
1926 int g_nullredirs;
1927 int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1928 struct var *vartab[VTABSIZE];
1929 struct var varinit[ARRAY_SIZE(varinit_data)];
1930};
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001931extern struct globals_var *const ash_ptr_to_globals_var;
1932#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00001933#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00001934//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00001935#define g_nullredirs (G_var.g_nullredirs )
1936#define preverrout_fd (G_var.preverrout_fd)
1937#define vartab (G_var.vartab )
1938#define varinit (G_var.varinit )
1939#define INIT_G_var() do { \
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00001940 unsigned i; \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001941 (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
1942 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001943 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001944 varinit[i].flags = varinit_data[i].flags; \
1945 varinit[i].var_text = varinit_data[i].var_text; \
1946 varinit[i].var_func = varinit_data[i].var_func; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001947 } \
1948} while (0)
1949
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001950#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001951#if ENABLE_ASH_MAIL
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001952# define vmail (&vifs)[1]
1953# define vmpath (&vmail)[1]
1954# define vpath (&vmpath)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001955#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001956# define vpath (&vifs)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001957#endif
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001958#define vps1 (&vpath)[1]
1959#define vps2 (&vps1)[1]
1960#define vps4 (&vps2)[1]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001961#if ENABLE_ASH_GETOPTS
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001962# define voptind (&vps4)[1]
1963# if ENABLE_ASH_RANDOM_SUPPORT
1964# define vrandom (&voptind)[1]
1965# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001966#else
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001967# if ENABLE_ASH_RANDOM_SUPPORT
1968# define vrandom (&vps4)[1]
1969# endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001971
1972/*
1973 * The following macros access the values of the above variables.
1974 * They have to skip over the name. They return the null string
1975 * for unset variables.
1976 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001977#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001978#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001979#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001980# define mailval() (vmail.var_text + 5)
1981# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001982# define mpathset() ((vmpath.flags & VUNSET) == 0)
1983#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001984#define pathval() (vpath.var_text + 5)
1985#define ps1val() (vps1.var_text + 4)
1986#define ps2val() (vps2.var_text + 4)
1987#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001988#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02001989# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00001990#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001991
Denis Vlasenko01631112007-12-16 17:20:38 +00001992#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02001993static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00001994getoptsreset(const char *value)
1995{
1996 shellparam.optind = number(value);
1997 shellparam.optoff = -1;
1998}
1999#endif
2000
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002001/* math.h has these, otherwise define our private copies */
2002#if !ENABLE_SH_MATH_SUPPORT
2003#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
2004#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002005/*
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002006 * Return the pointer to the first char which is not part of a legal variable name
2007 * (a letter or underscore followed by letters, underscores, and digits).
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002008 */
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002009static const char*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002010endofname(const char *name)
2011{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002012 if (!is_name(*name))
2013 return name;
2014 while (*++name) {
2015 if (!is_in_name(*name))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002016 break;
2017 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002018 return name;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002019}
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002020#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002021
2022/*
2023 * Compares two strings up to the first = or '\0'. The first
2024 * string must be terminated by '='; the second may be terminated by
2025 * either '=' or '\0'.
2026 */
2027static int
2028varcmp(const char *p, const char *q)
2029{
2030 int c, d;
2031
2032 while ((c = *p) == (d = *q)) {
2033 if (!c || c == '=')
2034 goto out;
2035 p++;
2036 q++;
2037 }
2038 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002039 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002040 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002041 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002042 out:
2043 return c - d;
2044}
2045
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002046/*
2047 * Find the appropriate entry in the hash table from the name.
2048 */
2049static struct var **
2050hashvar(const char *p)
2051{
2052 unsigned hashval;
2053
2054 hashval = ((unsigned char) *p) << 4;
2055 while (*p && *p != '=')
2056 hashval += (unsigned char) *p++;
2057 return &vartab[hashval % VTABSIZE];
2058}
2059
2060static int
2061vpcmp(const void *a, const void *b)
2062{
2063 return varcmp(*(const char **)a, *(const char **)b);
2064}
2065
2066/*
2067 * This routine initializes the builtin variables.
2068 */
2069static void
2070initvar(void)
2071{
2072 struct var *vp;
2073 struct var *end;
2074 struct var **vpp;
2075
2076 /*
2077 * PS1 depends on uid
2078 */
2079#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002080 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002081#else
2082 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002083 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084#endif
2085 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002086 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002087 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002088 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002089 vp->next = *vpp;
2090 *vpp = vp;
2091 } while (++vp < end);
2092}
2093
2094static struct var **
2095findvar(struct var **vpp, const char *name)
2096{
2097 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002098 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002099 break;
2100 }
2101 }
2102 return vpp;
2103}
2104
2105/*
2106 * Find the value of a variable. Returns NULL if not set.
2107 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002108static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002109lookupvar(const char *name)
2110{
2111 struct var *v;
2112
2113 v = *findvar(hashvar(name), name);
2114 if (v) {
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002115#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002116 /*
2117 * Dynamic variables are implemented roughly the same way they are
2118 * in bash. Namely, they're "special" so long as they aren't unset.
2119 * As soon as they're unset, they're no longer dynamic, and dynamic
2120 * lookup will no longer happen at that point. -- PFM.
2121 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002122 if (v->flags & VDYNAMIC)
2123 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002124#endif
2125 if (!(v->flags & VUNSET))
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002126 return var_end(v->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002127 }
2128 return NULL;
2129}
2130
2131/*
2132 * Search the environment of a builtin command.
2133 */
Mike Frysinger98c52642009-04-02 10:02:37 +00002134static const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002135bltinlookup(const char *name)
2136{
2137 struct strlist *sp;
2138
2139 for (sp = cmdenviron; sp; sp = sp->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002140 if (varcmp(sp->text, name) == 0)
2141 return var_end(sp->text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002142 }
2143 return lookupvar(name);
2144}
2145
2146/*
2147 * Same as setvar except that the variable and value are passed in
2148 * the first argument as name=value. Since the first argument will
2149 * be actually stored in the table, it should not be a string that
2150 * will go away.
2151 * Called with interrupts off.
2152 */
2153static void
2154setvareq(char *s, int flags)
2155{
2156 struct var *vp, **vpp;
2157
2158 vpp = hashvar(s);
2159 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2160 vp = *findvar(vpp, s);
2161 if (vp) {
2162 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2163 const char *n;
2164
2165 if (flags & VNOSAVE)
2166 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002167 n = vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002168 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2169 }
2170
2171 if (flags & VNOSET)
2172 return;
2173
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002174 if (vp->var_func && !(flags & VNOFUNC))
2175 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002176
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002177 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2178 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002179
2180 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
2181 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002182 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002183 if (flags & VNOSET)
2184 return;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002185 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002186 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002187 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002188 *vpp = vp;
2189 }
2190 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2191 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002192 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193 vp->flags = flags;
2194}
2195
2196/*
2197 * Set the value of a variable. The flags argument is ored with the
2198 * flags of the variable. If val is NULL, the variable is unset.
2199 */
2200static void
2201setvar(const char *name, const char *val, int flags)
2202{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002203 const char *q;
2204 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002205 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002206 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002207 size_t vallen;
2208
2209 q = endofname(name);
2210 p = strchrnul(q, '=');
2211 namelen = p - name;
2212 if (!namelen || p != q)
2213 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2214 vallen = 0;
2215 if (val == NULL) {
2216 flags |= VUNSET;
2217 } else {
2218 vallen = strlen(val);
2219 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002220
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002221 INT_OFF;
2222 nameeq = ckmalloc(namelen + vallen + 2);
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002223 p = memcpy(nameeq, name, namelen) + namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002224 if (val) {
2225 *p++ = '=';
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002226 p = memcpy(p, val, vallen) + vallen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002227 }
2228 *p = '\0';
2229 setvareq(nameeq, flags | VNOSAVE);
2230 INT_ON;
2231}
2232
Denys Vlasenko03dad222010-01-12 23:29:57 +01002233static void FAST_FUNC
2234setvar2(const char *name, const char *val)
2235{
2236 setvar(name, val, 0);
2237}
2238
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002239#if ENABLE_ASH_GETOPTS
2240/*
2241 * Safe version of setvar, returns 1 on success 0 on failure.
2242 */
2243static int
2244setvarsafe(const char *name, const char *val, int flags)
2245{
2246 int err;
2247 volatile int saveint;
2248 struct jmploc *volatile savehandler = exception_handler;
2249 struct jmploc jmploc;
2250
2251 SAVE_INT(saveint);
2252 if (setjmp(jmploc.loc))
2253 err = 1;
2254 else {
2255 exception_handler = &jmploc;
2256 setvar(name, val, flags);
2257 err = 0;
2258 }
2259 exception_handler = savehandler;
2260 RESTORE_INT(saveint);
2261 return err;
2262}
2263#endif
2264
2265/*
2266 * Unset the specified variable.
2267 */
2268static int
2269unsetvar(const char *s)
2270{
2271 struct var **vpp;
2272 struct var *vp;
2273 int retval;
2274
2275 vpp = findvar(hashvar(s), s);
2276 vp = *vpp;
2277 retval = 2;
2278 if (vp) {
2279 int flags = vp->flags;
2280
2281 retval = 1;
2282 if (flags & VREADONLY)
2283 goto out;
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002284#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002285 vp->flags &= ~VDYNAMIC;
2286#endif
2287 if (flags & VUNSET)
2288 goto ok;
2289 if ((flags & VSTRFIXED) == 0) {
2290 INT_OFF;
2291 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002292 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002293 *vpp = vp->next;
2294 free(vp);
2295 INT_ON;
2296 } else {
2297 setvar(s, 0, 0);
2298 vp->flags &= ~VEXPORT;
2299 }
2300 ok:
2301 retval = 0;
2302 }
2303 out:
2304 return retval;
2305}
2306
2307/*
2308 * Process a linked list of variable assignments.
2309 */
2310static void
2311listsetvar(struct strlist *list_set_var, int flags)
2312{
2313 struct strlist *lp = list_set_var;
2314
2315 if (!lp)
2316 return;
2317 INT_OFF;
2318 do {
2319 setvareq(lp->text, flags);
Denis Vlasenko9650f362007-02-23 01:04:37 +00002320 lp = lp->next;
2321 } while (lp);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002322 INT_ON;
2323}
2324
2325/*
2326 * Generate a list of variables satisfying the given conditions.
2327 */
2328static char **
2329listvars(int on, int off, char ***end)
2330{
2331 struct var **vpp;
2332 struct var *vp;
2333 char **ep;
2334 int mask;
2335
2336 STARTSTACKSTR(ep);
2337 vpp = vartab;
2338 mask = on | off;
2339 do {
2340 for (vp = *vpp; vp; vp = vp->next) {
2341 if ((vp->flags & mask) == on) {
2342 if (ep == stackstrend())
2343 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002344 *ep++ = (char*)vp->var_text;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002345 }
2346 }
2347 } while (++vpp < vartab + VTABSIZE);
2348 if (ep == stackstrend())
2349 ep = growstackstr();
2350 if (end)
2351 *end = ep;
2352 *ep++ = NULL;
2353 return grabstackstr(ep);
2354}
2355
2356
2357/* ============ Path search helper
2358 *
2359 * The variable path (passed by reference) should be set to the start
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002360 * of the path before the first call; path_advance will update
2361 * this value as it proceeds. Successive calls to path_advance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002362 * the possible path expansions in sequence. If an option (indicated by
2363 * a percent sign) appears in the path entry then the global variable
2364 * pathopt will be set to point to it; otherwise pathopt will be set to
2365 * NULL.
2366 */
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002367static const char *pathopt; /* set by path_advance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002368
2369static char *
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002370path_advance(const char **path, const char *name)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002371{
2372 const char *p;
2373 char *q;
2374 const char *start;
2375 size_t len;
2376
2377 if (*path == NULL)
2378 return NULL;
2379 start = *path;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002380 for (p = start; *p && *p != ':' && *p != '%'; p++)
2381 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002382 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2383 while (stackblocksize() < len)
2384 growstackblock();
2385 q = stackblock();
2386 if (p != start) {
2387 memcpy(q, start, p - start);
2388 q += p - start;
2389 *q++ = '/';
2390 }
2391 strcpy(q, name);
2392 pathopt = NULL;
2393 if (*p == '%') {
2394 pathopt = ++p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00002395 while (*p && *p != ':')
2396 p++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002397 }
2398 if (*p == ':')
2399 *path = p + 1;
2400 else
2401 *path = NULL;
2402 return stalloc(len);
2403}
2404
2405
2406/* ============ Prompt */
2407
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002408static smallint doprompt; /* if set, prompt the user */
2409static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002410
2411#if ENABLE_FEATURE_EDITING
2412static line_input_t *line_input_state;
2413static const char *cmdedit_prompt;
2414static void
2415putprompt(const char *s)
2416{
2417 if (ENABLE_ASH_EXPAND_PRMT) {
2418 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002419 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002420 return;
2421 }
2422 cmdedit_prompt = s;
2423}
2424#else
2425static void
2426putprompt(const char *s)
2427{
2428 out2str(s);
2429}
2430#endif
2431
2432#if ENABLE_ASH_EXPAND_PRMT
2433/* expandstr() needs parsing machinery, so it is far away ahead... */
2434static const char *expandstr(const char *ps);
2435#else
2436#define expandstr(s) s
2437#endif
2438
2439static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002440setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002441{
2442 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002443 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2444
2445 if (!do_set)
2446 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002447
2448 needprompt = 0;
2449
2450 switch (whichprompt) {
2451 case 1:
2452 prompt = ps1val();
2453 break;
2454 case 2:
2455 prompt = ps2val();
2456 break;
2457 default: /* 0 */
2458 prompt = nullstr;
2459 }
2460#if ENABLE_ASH_EXPAND_PRMT
2461 setstackmark(&smark);
2462 stalloc(stackblocksize());
2463#endif
2464 putprompt(expandstr(prompt));
2465#if ENABLE_ASH_EXPAND_PRMT
2466 popstackmark(&smark);
2467#endif
2468}
2469
2470
2471/* ============ The cd and pwd commands */
2472
2473#define CD_PHYSICAL 1
2474#define CD_PRINT 2
2475
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002476static int
2477cdopt(void)
2478{
2479 int flags = 0;
2480 int i, j;
2481
2482 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002483 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002484 if (i != j) {
2485 flags ^= CD_PHYSICAL;
2486 j = i;
2487 }
2488 }
2489
2490 return flags;
2491}
2492
2493/*
2494 * Update curdir (the name of the current directory) in response to a
2495 * cd command.
2496 */
2497static const char *
2498updatepwd(const char *dir)
2499{
2500 char *new;
2501 char *p;
2502 char *cdcomppath;
2503 const char *lim;
2504
2505 cdcomppath = ststrdup(dir);
2506 STARTSTACKSTR(new);
2507 if (*dir != '/') {
2508 if (curdir == nullstr)
2509 return 0;
2510 new = stack_putstr(curdir, new);
2511 }
2512 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002513 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002514 if (*dir != '/') {
2515 if (new[-1] != '/')
2516 USTPUTC('/', new);
2517 if (new > lim && *lim == '/')
2518 lim++;
2519 } else {
2520 USTPUTC('/', new);
2521 cdcomppath++;
2522 if (dir[1] == '/' && dir[2] != '/') {
2523 USTPUTC('/', new);
2524 cdcomppath++;
2525 lim++;
2526 }
2527 }
2528 p = strtok(cdcomppath, "/");
2529 while (p) {
2530 switch (*p) {
2531 case '.':
2532 if (p[1] == '.' && p[2] == '\0') {
2533 while (new > lim) {
2534 STUNPUTC(new);
2535 if (new[-1] == '/')
2536 break;
2537 }
2538 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002539 }
2540 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002541 break;
2542 /* fall through */
2543 default:
2544 new = stack_putstr(p, new);
2545 USTPUTC('/', new);
2546 }
2547 p = strtok(0, "/");
2548 }
2549 if (new > lim)
2550 STUNPUTC(new);
2551 *new = 0;
2552 return stackblock();
2553}
2554
2555/*
2556 * Find out what the current directory is. If we already know the current
2557 * directory, this routine returns immediately.
2558 */
2559static char *
2560getpwd(void)
2561{
Denis Vlasenko01631112007-12-16 17:20:38 +00002562 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002563 return dir ? dir : nullstr;
2564}
2565
2566static void
2567setpwd(const char *val, int setold)
2568{
2569 char *oldcur, *dir;
2570
2571 oldcur = dir = curdir;
2572
2573 if (setold) {
2574 setvar("OLDPWD", oldcur, VEXPORT);
2575 }
2576 INT_OFF;
2577 if (physdir != nullstr) {
2578 if (physdir != oldcur)
2579 free(physdir);
2580 physdir = nullstr;
2581 }
2582 if (oldcur == val || !val) {
2583 char *s = getpwd();
2584 physdir = s;
2585 if (!val)
2586 dir = s;
2587 } else
2588 dir = ckstrdup(val);
2589 if (oldcur != dir && oldcur != nullstr) {
2590 free(oldcur);
2591 }
2592 curdir = dir;
2593 INT_ON;
2594 setvar("PWD", dir, VEXPORT);
2595}
2596
2597static void hashcd(void);
2598
2599/*
2600 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2601 * know that the current directory has changed.
2602 */
2603static int
2604docd(const char *dest, int flags)
2605{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002606 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002607 int err;
2608
2609 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2610
2611 INT_OFF;
2612 if (!(flags & CD_PHYSICAL)) {
2613 dir = updatepwd(dest);
2614 if (dir)
2615 dest = dir;
2616 }
2617 err = chdir(dest);
2618 if (err)
2619 goto out;
2620 setpwd(dir, 1);
2621 hashcd();
2622 out:
2623 INT_ON;
2624 return err;
2625}
2626
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002627static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002628cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002629{
2630 const char *dest;
2631 const char *path;
2632 const char *p;
2633 char c;
2634 struct stat statb;
2635 int flags;
2636
2637 flags = cdopt();
2638 dest = *argptr;
2639 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002640 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002641 else if (LONE_DASH(dest)) {
2642 dest = bltinlookup("OLDPWD");
2643 flags |= CD_PRINT;
2644 }
2645 if (!dest)
2646 dest = nullstr;
2647 if (*dest == '/')
2648 goto step7;
2649 if (*dest == '.') {
2650 c = dest[1];
2651 dotdot:
2652 switch (c) {
2653 case '\0':
2654 case '/':
2655 goto step6;
2656 case '.':
2657 c = dest[2];
2658 if (c != '.')
2659 goto dotdot;
2660 }
2661 }
2662 if (!*dest)
2663 dest = ".";
2664 path = bltinlookup("CDPATH");
2665 if (!path) {
2666 step6:
2667 step7:
2668 p = dest;
2669 goto docd;
2670 }
2671 do {
2672 c = *path;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02002673 p = path_advance(&path, dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002674 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2675 if (c && c != ':')
2676 flags |= CD_PRINT;
2677 docd:
2678 if (!docd(p, flags))
2679 goto out;
2680 break;
2681 }
2682 } while (path);
2683 ash_msg_and_raise_error("can't cd to %s", dest);
2684 /* NOTREACHED */
2685 out:
2686 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002687 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002688 return 0;
2689}
2690
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002691static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002692pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002693{
2694 int flags;
2695 const char *dir = curdir;
2696
2697 flags = cdopt();
2698 if (flags) {
2699 if (physdir == nullstr)
2700 setpwd(dir, 0);
2701 dir = physdir;
2702 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002703 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002704 return 0;
2705}
2706
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002707
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002708/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002709
Denis Vlasenko834dee72008-10-07 09:18:30 +00002710
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002711#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002712
Eric Andersenc470f442003-07-28 09:56:35 +00002713/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002714#define CWORD 0 /* character is nothing special */
2715#define CNL 1 /* newline character */
2716#define CBACK 2 /* a backslash character */
2717#define CSQUOTE 3 /* single quote */
2718#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002719#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002720#define CBQUOTE 6 /* backwards single quote */
2721#define CVAR 7 /* a dollar sign */
2722#define CENDVAR 8 /* a '}' character */
2723#define CLP 9 /* a left paren in arithmetic */
2724#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002725#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002726#define CCTL 12 /* like CWORD, except it must be escaped */
2727#define CSPCL 13 /* these terminate a word */
2728#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002729
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002730#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002731#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002732# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002733#endif
2734
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002735#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002736
Mike Frysinger98c52642009-04-02 10:02:37 +00002737#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002738# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002739#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002740# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002741#endif
Denys Vlasenko068d3862009-11-29 01:41:11 +01002742static const uint16_t S_I_T[] = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002743#if ENABLE_ASH_ALIAS
2744 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2745#endif
2746 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2747 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2748 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
2749 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
2750 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
2751 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
2752 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
2753 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
2754 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
2755 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
2756 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002757#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002758 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
2759 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
2760 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
2761#endif
2762#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00002763};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002764/* Constants below must match table above */
2765enum {
2766#if ENABLE_ASH_ALIAS
2767 CSPCL_CIGN_CIGN_CIGN , /* 0 */
2768#endif
2769 CSPCL_CWORD_CWORD_CWORD , /* 1 */
2770 CNL_CNL_CNL_CNL , /* 2 */
2771 CWORD_CCTL_CCTL_CWORD , /* 3 */
2772 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
2773 CVAR_CVAR_CWORD_CVAR , /* 5 */
2774 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
2775 CSPCL_CWORD_CWORD_CLP , /* 7 */
2776 CSPCL_CWORD_CWORD_CRP , /* 8 */
2777 CBACK_CBACK_CCTL_CBACK , /* 9 */
2778 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
2779 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
2780 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
2781 CWORD_CWORD_CWORD_CWORD , /* 13 */
2782 CCTL_CCTL_CCTL_CCTL , /* 14 */
2783};
Eric Andersen2870d962001-07-02 17:27:21 +00002784
Denys Vlasenkocd716832009-11-28 22:14:02 +01002785/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
2786 * caller must ensure proper cast on it if c is *char_ptr!
2787 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002788/* Values for syntax param */
2789#define BASESYNTAX 0 /* not in quotes */
2790#define DQSYNTAX 1 /* in double quotes */
2791#define SQSYNTAX 2 /* in single quotes */
2792#define ARISYNTAX 3 /* in arithmetic */
2793#define PSSYNTAX 4 /* prompt. never passed to SIT() */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002794
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002795#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002796
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002797static int
2798SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002799{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00002800 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
Denys Vlasenkocd716832009-11-28 22:14:02 +01002801# if ENABLE_ASH_ALIAS
2802 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002803 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
2804 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
2805 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
2806 11, 3 /* "}~" */
2807 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002808# else
2809 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00002810 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
2811 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2812 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
2813 10, 2 /* "}~" */
2814 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01002815# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002816 const char *s;
2817 int indx;
2818
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002819 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002820 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01002821# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002822 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002823 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002824 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01002825# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002826 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002827 /* Cast is purely for paranoia here,
2828 * just in case someone passed signed char to us */
2829 if ((unsigned char)c >= CTL_FIRST
2830 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002831 ) {
2832 return CCTL;
2833 }
2834 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002835 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002836 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00002837 indx = syntax_index_table[s - spec_symbls];
2838 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002839 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002840}
2841
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002842#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002843
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002844static const uint8_t syntax_index_table[] = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00002845 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01002846 /* 0 */ CWORD_CWORD_CWORD_CWORD,
2847 /* 1 */ CWORD_CWORD_CWORD_CWORD,
2848 /* 2 */ CWORD_CWORD_CWORD_CWORD,
2849 /* 3 */ CWORD_CWORD_CWORD_CWORD,
2850 /* 4 */ CWORD_CWORD_CWORD_CWORD,
2851 /* 5 */ CWORD_CWORD_CWORD_CWORD,
2852 /* 6 */ CWORD_CWORD_CWORD_CWORD,
2853 /* 7 */ CWORD_CWORD_CWORD_CWORD,
2854 /* 8 */ CWORD_CWORD_CWORD_CWORD,
2855 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
2856 /* 10 "\n" */ CNL_CNL_CNL_CNL,
2857 /* 11 */ CWORD_CWORD_CWORD_CWORD,
2858 /* 12 */ CWORD_CWORD_CWORD_CWORD,
2859 /* 13 */ CWORD_CWORD_CWORD_CWORD,
2860 /* 14 */ CWORD_CWORD_CWORD_CWORD,
2861 /* 15 */ CWORD_CWORD_CWORD_CWORD,
2862 /* 16 */ CWORD_CWORD_CWORD_CWORD,
2863 /* 17 */ CWORD_CWORD_CWORD_CWORD,
2864 /* 18 */ CWORD_CWORD_CWORD_CWORD,
2865 /* 19 */ CWORD_CWORD_CWORD_CWORD,
2866 /* 20 */ CWORD_CWORD_CWORD_CWORD,
2867 /* 21 */ CWORD_CWORD_CWORD_CWORD,
2868 /* 22 */ CWORD_CWORD_CWORD_CWORD,
2869 /* 23 */ CWORD_CWORD_CWORD_CWORD,
2870 /* 24 */ CWORD_CWORD_CWORD_CWORD,
2871 /* 25 */ CWORD_CWORD_CWORD_CWORD,
2872 /* 26 */ CWORD_CWORD_CWORD_CWORD,
2873 /* 27 */ CWORD_CWORD_CWORD_CWORD,
2874 /* 28 */ CWORD_CWORD_CWORD_CWORD,
2875 /* 29 */ CWORD_CWORD_CWORD_CWORD,
2876 /* 30 */ CWORD_CWORD_CWORD_CWORD,
2877 /* 31 */ CWORD_CWORD_CWORD_CWORD,
2878 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
2879 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
2880 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
2881 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
2882 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
2883 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
2884 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
2885 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
2886 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
2887 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
2888 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
2889 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
2890 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
2891 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
2892 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
2893 /* 47 "/" */ CWORD_CCTL_CCTL_CWORD,
2894 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
2895 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
2896 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
2897 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
2898 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
2899 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
2900 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
2901 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
2902 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
2903 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
2904 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
2905 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
2906 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
2907 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
2908 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
2909 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
2910 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
2911 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
2912 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
2913 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
2914 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
2915 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
2916 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
2917 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
2918 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
2919 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
2920 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
2921 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
2922 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
2923 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
2924 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
2925 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
2926 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
2927 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
2928 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
2929 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
2930 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
2931 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
2932 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
2933 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
2934 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
2935 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
2936 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
2937 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
2938 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
2939 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
2940 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
2941 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
2942 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
2943 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
2944 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
2945 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
2946 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
2947 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
2948 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
2949 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
2950 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
2951 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
2952 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
2953 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
2954 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
2955 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
2956 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
2957 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
2958 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
2959 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
2960 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
2961 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
2962 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
2963 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
2964 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
2965 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
2966 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
2967 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
2968 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
2969 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
2970 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
2971 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
2972 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
2973 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
2974 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
2975 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
2976 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
2977 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
2978 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
2979 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
2980 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
2981 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
2982 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
2983 /* 137 */ CWORD_CWORD_CWORD_CWORD,
2984 /* 138 */ CWORD_CWORD_CWORD_CWORD,
2985 /* 139 */ CWORD_CWORD_CWORD_CWORD,
2986 /* 140 */ CWORD_CWORD_CWORD_CWORD,
2987 /* 141 */ CWORD_CWORD_CWORD_CWORD,
2988 /* 142 */ CWORD_CWORD_CWORD_CWORD,
2989 /* 143 */ CWORD_CWORD_CWORD_CWORD,
2990 /* 144 */ CWORD_CWORD_CWORD_CWORD,
2991 /* 145 */ CWORD_CWORD_CWORD_CWORD,
2992 /* 146 */ CWORD_CWORD_CWORD_CWORD,
2993 /* 147 */ CWORD_CWORD_CWORD_CWORD,
2994 /* 148 */ CWORD_CWORD_CWORD_CWORD,
2995 /* 149 */ CWORD_CWORD_CWORD_CWORD,
2996 /* 150 */ CWORD_CWORD_CWORD_CWORD,
2997 /* 151 */ CWORD_CWORD_CWORD_CWORD,
2998 /* 152 */ CWORD_CWORD_CWORD_CWORD,
2999 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3000 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3001 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3002 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3003 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3004 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3005 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3006 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3007 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3008 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3009 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3010 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3011 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3012 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3013 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3014 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3015 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3016 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3017 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3018 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3019 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3020 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3021 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3022 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3023 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3024 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3025 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3026 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3027 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3028 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3029 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3030 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3031 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3032 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3033 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3034 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3035 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3036 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3037 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3038 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3039 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3040 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3041 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3042 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3043 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3044 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3045 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3046 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3047 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3048 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3049 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3050 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3051 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3052 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3053 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3054 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3055 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3056 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3057 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3058 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3059 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3060 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3061 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3062 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3063 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3064 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3065 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3066 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3067 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3068 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3069 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3070 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3071 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3072 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3073 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3074 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3075 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3076 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3077 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3078 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3079 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3080 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3081 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3082 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3083 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3084 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3085 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3086 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3087 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3088 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3089 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3090 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3091 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3092 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3093 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3094 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3095 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3096 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3097 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3098 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3099 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003102 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003103# if ENABLE_ASH_ALIAS
3104 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3105# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003106};
3107
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003108# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003109
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003110#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003111
Eric Andersen2870d962001-07-02 17:27:21 +00003112
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003113/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003114
Denis Vlasenko131ae172007-02-18 13:00:19 +00003115#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003116
3117#define ALIASINUSE 1
3118#define ALIASDEAD 2
3119
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003120struct alias {
3121 struct alias *next;
3122 char *name;
3123 char *val;
3124 int flag;
3125};
3126
Denis Vlasenko01631112007-12-16 17:20:38 +00003127
3128static struct alias **atab; // [ATABSIZE];
3129#define INIT_G_alias() do { \
3130 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3131} while (0)
3132
Eric Andersen2870d962001-07-02 17:27:21 +00003133
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003134static struct alias **
3135__lookupalias(const char *name) {
3136 unsigned int hashval;
3137 struct alias **app;
3138 const char *p;
3139 unsigned int ch;
3140
3141 p = name;
3142
3143 ch = (unsigned char)*p;
3144 hashval = ch << 4;
3145 while (ch) {
3146 hashval += ch;
3147 ch = (unsigned char)*++p;
3148 }
3149 app = &atab[hashval % ATABSIZE];
3150
3151 for (; *app; app = &(*app)->next) {
3152 if (strcmp(name, (*app)->name) == 0) {
3153 break;
3154 }
3155 }
3156
3157 return app;
3158}
3159
3160static struct alias *
3161lookupalias(const char *name, int check)
3162{
3163 struct alias *ap = *__lookupalias(name);
3164
3165 if (check && ap && (ap->flag & ALIASINUSE))
3166 return NULL;
3167 return ap;
3168}
3169
3170static struct alias *
3171freealias(struct alias *ap)
3172{
3173 struct alias *next;
3174
3175 if (ap->flag & ALIASINUSE) {
3176 ap->flag |= ALIASDEAD;
3177 return ap;
3178 }
3179
3180 next = ap->next;
3181 free(ap->name);
3182 free(ap->val);
3183 free(ap);
3184 return next;
3185}
Eric Andersencb57d552001-06-28 07:25:16 +00003186
Eric Andersenc470f442003-07-28 09:56:35 +00003187static void
3188setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003189{
3190 struct alias *ap, **app;
3191
3192 app = __lookupalias(name);
3193 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003194 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003195 if (ap) {
3196 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003197 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003198 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003199 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003200 ap->flag &= ~ALIASDEAD;
3201 } else {
3202 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003203 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003204 ap->name = ckstrdup(name);
3205 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003206 /*ap->flag = 0; - ckzalloc did it */
3207 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003208 *app = ap;
3209 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003210 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003211}
3212
Eric Andersenc470f442003-07-28 09:56:35 +00003213static int
3214unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003215{
Eric Andersencb57d552001-06-28 07:25:16 +00003216 struct alias **app;
3217
3218 app = __lookupalias(name);
3219
3220 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003221 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003222 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003223 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003224 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003225 }
3226
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003227 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003228}
3229
Eric Andersenc470f442003-07-28 09:56:35 +00003230static void
3231rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003232{
Eric Andersencb57d552001-06-28 07:25:16 +00003233 struct alias *ap, **app;
3234 int i;
3235
Denis Vlasenkob012b102007-02-19 22:43:01 +00003236 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003237 for (i = 0; i < ATABSIZE; i++) {
3238 app = &atab[i];
3239 for (ap = *app; ap; ap = *app) {
3240 *app = freealias(*app);
3241 if (ap == *app) {
3242 app = &ap->next;
3243 }
3244 }
3245 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003246 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003247}
3248
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003249static void
3250printalias(const struct alias *ap)
3251{
3252 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3253}
3254
Eric Andersencb57d552001-06-28 07:25:16 +00003255/*
3256 * TODO - sort output
3257 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003258static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003259aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003260{
3261 char *n, *v;
3262 int ret = 0;
3263 struct alias *ap;
3264
Denis Vlasenko68404f12008-03-17 09:00:54 +00003265 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003266 int i;
3267
Denis Vlasenko68404f12008-03-17 09:00:54 +00003268 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003269 for (ap = atab[i]; ap; ap = ap->next) {
3270 printalias(ap);
3271 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003272 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003273 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003274 }
3275 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003276 v = strchr(n+1, '=');
3277 if (v == NULL) { /* n+1: funny ksh stuff */
3278 ap = *__lookupalias(n);
3279 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003280 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003281 ret = 1;
3282 } else
3283 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003284 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003285 *v++ = '\0';
3286 setalias(n, v);
3287 }
3288 }
3289
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003290 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003291}
3292
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003293static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003294unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003295{
3296 int i;
3297
3298 while ((i = nextopt("a")) != '\0') {
3299 if (i == 'a') {
3300 rmaliases();
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003301 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003302 }
3303 }
3304 for (i = 0; *argptr; argptr++) {
3305 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003306 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003307 i = 1;
3308 }
3309 }
3310
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003311 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003312}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003313
Denis Vlasenko131ae172007-02-18 13:00:19 +00003314#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003315
Eric Andersenc470f442003-07-28 09:56:35 +00003316
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003317/* ============ jobs.c */
3318
3319/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003320#define FORK_FG 0
3321#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003322#define FORK_NOJOB 2
3323
3324/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003325#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3326#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3327#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003328
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003329/*
3330 * A job structure contains information about a job. A job is either a
3331 * single process or a set of processes contained in a pipeline. In the
3332 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3333 * array of pids.
3334 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003335struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003336 pid_t ps_pid; /* process id */
3337 int ps_status; /* last process status from wait() */
3338 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003339};
3340
3341struct job {
3342 struct procstat ps0; /* status of process */
3343 struct procstat *ps; /* status or processes when more than one */
3344#if JOBS
3345 int stopstatus; /* status of a stopped job */
3346#endif
3347 uint32_t
3348 nprocs: 16, /* number of processes */
3349 state: 8,
3350#define JOBRUNNING 0 /* at least one proc running */
3351#define JOBSTOPPED 1 /* all procs are stopped */
3352#define JOBDONE 2 /* all procs are completed */
3353#if JOBS
3354 sigint: 1, /* job was killed by SIGINT */
3355 jobctl: 1, /* job running under job control */
3356#endif
3357 waited: 1, /* true if this entry has been waited for */
3358 used: 1, /* true if this entry is in used */
3359 changed: 1; /* true if status has changed */
3360 struct job *prev_job; /* previous job */
3361};
3362
Denis Vlasenko68404f12008-03-17 09:00:54 +00003363static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003364static int forkshell(struct job *, union node *, int);
3365static int waitforjob(struct job *);
3366
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003367#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003368enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003369#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003370#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003371static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003372static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003373#endif
3374
3375/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003376 * Ignore a signal.
3377 */
3378static void
3379ignoresig(int signo)
3380{
3381 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3382 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3383 /* No, need to do it */
3384 signal(signo, SIG_IGN);
3385 }
3386 sigmode[signo - 1] = S_HARD_IGN;
3387}
3388
3389/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003390 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003391 */
3392static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003393signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003394{
3395 gotsig[signo - 1] = 1;
3396
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003397 if (signo == SIGINT && !trap[SIGINT]) {
3398 if (!suppress_int) {
3399 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003400 raise_interrupt(); /* does not return */
3401 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003402 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003403 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003404 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003405 }
3406}
3407
3408/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003409 * Set the signal handler for the specified signal. The routine figures
3410 * out what it should be set to.
3411 */
3412static void
3413setsignal(int signo)
3414{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003415 char *t;
3416 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003417 struct sigaction act;
3418
3419 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003420 new_act = S_DFL;
3421 if (t != NULL) { /* trap for this sig is set */
3422 new_act = S_CATCH;
3423 if (t[0] == '\0') /* trap is "": ignore this sig */
3424 new_act = S_IGN;
3425 }
3426
3427 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003428 switch (signo) {
3429 case SIGINT:
3430 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003431 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003432 break;
3433 case SIGQUIT:
3434#if DEBUG
3435 if (debug)
3436 break;
3437#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003438 /* man bash:
3439 * "In all cases, bash ignores SIGQUIT. Non-builtin
3440 * commands run by bash have signal handlers
3441 * set to the values inherited by the shell
3442 * from its parent". */
3443 new_act = S_IGN;
3444 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003445 case SIGTERM:
3446 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003447 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003448 break;
3449#if JOBS
3450 case SIGTSTP:
3451 case SIGTTOU:
3452 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003453 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003454 break;
3455#endif
3456 }
3457 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003458//TODO: if !rootshell, we reset SIGQUIT to DFL,
3459//whereas we have to restore it to what shell got on entry
3460//from the parent. See comment above
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003461
3462 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003463 cur_act = *t;
3464 if (cur_act == 0) {
3465 /* current setting is not yet known */
3466 if (sigaction(signo, NULL, &act)) {
3467 /* pretend it worked; maybe we should give a warning,
3468 * but other shells don't. We don't alter sigmode,
3469 * so we retry every time.
3470 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003471 return;
3472 }
3473 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003474 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003475 if (mflag
3476 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3477 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003478 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003479 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003480 }
3481 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003482 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003483 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003484
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003485 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003486 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003487 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003488 act.sa_handler = signal_handler;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003489 act.sa_flags = 0; /* matters only if !DFL and !IGN */
3490 sigfillset(&act.sa_mask); /* ditto */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003491 break;
3492 case S_IGN:
3493 act.sa_handler = SIG_IGN;
3494 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003495 }
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003496 sigaction_set(signo, &act);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003497
3498 *t = new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003499}
3500
3501/* mode flags for set_curjob */
3502#define CUR_DELETE 2
3503#define CUR_RUNNING 1
3504#define CUR_STOPPED 0
3505
3506/* mode flags for dowait */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003507#define DOWAIT_NONBLOCK WNOHANG
3508#define DOWAIT_BLOCK 0
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003509
3510#if JOBS
3511/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003512static int initialpgrp; //references:2
3513static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003514#endif
3515/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003516static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003517/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003518static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003519/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003520static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003521/* number of presumed living untracked jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003522static int jobless; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003523
3524static void
3525set_curjob(struct job *jp, unsigned mode)
3526{
3527 struct job *jp1;
3528 struct job **jpp, **curp;
3529
3530 /* first remove from list */
3531 jpp = curp = &curjob;
3532 do {
3533 jp1 = *jpp;
3534 if (jp1 == jp)
3535 break;
3536 jpp = &jp1->prev_job;
3537 } while (1);
3538 *jpp = jp1->prev_job;
3539
3540 /* Then re-insert in correct position */
3541 jpp = curp;
3542 switch (mode) {
3543 default:
3544#if DEBUG
3545 abort();
3546#endif
3547 case CUR_DELETE:
3548 /* job being deleted */
3549 break;
3550 case CUR_RUNNING:
3551 /* newly created job or backgrounded job,
3552 put after all stopped jobs. */
3553 do {
3554 jp1 = *jpp;
3555#if JOBS
3556 if (!jp1 || jp1->state != JOBSTOPPED)
3557#endif
3558 break;
3559 jpp = &jp1->prev_job;
3560 } while (1);
3561 /* FALLTHROUGH */
3562#if JOBS
3563 case CUR_STOPPED:
3564#endif
3565 /* newly stopped job - becomes curjob */
3566 jp->prev_job = *jpp;
3567 *jpp = jp;
3568 break;
3569 }
3570}
3571
3572#if JOBS || DEBUG
3573static int
3574jobno(const struct job *jp)
3575{
3576 return jp - jobtab + 1;
3577}
3578#endif
3579
3580/*
3581 * Convert a job name to a job structure.
3582 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003583#if !JOBS
3584#define getjob(name, getctl) getjob(name)
3585#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003586static struct job *
3587getjob(const char *name, int getctl)
3588{
3589 struct job *jp;
3590 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003591 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003592 unsigned num;
3593 int c;
3594 const char *p;
3595 char *(*match)(const char *, const char *);
3596
3597 jp = curjob;
3598 p = name;
3599 if (!p)
3600 goto currentjob;
3601
3602 if (*p != '%')
3603 goto err;
3604
3605 c = *++p;
3606 if (!c)
3607 goto currentjob;
3608
3609 if (!p[1]) {
3610 if (c == '+' || c == '%') {
3611 currentjob:
3612 err_msg = "No current job";
3613 goto check;
3614 }
3615 if (c == '-') {
3616 if (jp)
3617 jp = jp->prev_job;
3618 err_msg = "No previous job";
3619 check:
3620 if (!jp)
3621 goto err;
3622 goto gotit;
3623 }
3624 }
3625
3626 if (is_number(p)) {
3627 num = atoi(p);
3628 if (num < njobs) {
3629 jp = jobtab + num - 1;
3630 if (jp->used)
3631 goto gotit;
3632 goto err;
3633 }
3634 }
3635
3636 match = prefix;
3637 if (*p == '?') {
3638 match = strstr;
3639 p++;
3640 }
3641
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003642 found = NULL;
3643 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003644 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003645 if (found)
3646 goto err;
3647 found = jp;
3648 err_msg = "%s: ambiguous";
3649 }
3650 jp = jp->prev_job;
3651 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003652 if (!found)
3653 goto err;
3654 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655
3656 gotit:
3657#if JOBS
3658 err_msg = "job %s not created under job control";
3659 if (getctl && jp->jobctl == 0)
3660 goto err;
3661#endif
3662 return jp;
3663 err:
3664 ash_msg_and_raise_error(err_msg, name);
3665}
3666
3667/*
3668 * Mark a job structure as unused.
3669 */
3670static void
3671freejob(struct job *jp)
3672{
3673 struct procstat *ps;
3674 int i;
3675
3676 INT_OFF;
3677 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003678 if (ps->ps_cmd != nullstr)
3679 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003680 }
3681 if (jp->ps != &jp->ps0)
3682 free(jp->ps);
3683 jp->used = 0;
3684 set_curjob(jp, CUR_DELETE);
3685 INT_ON;
3686}
3687
3688#if JOBS
3689static void
3690xtcsetpgrp(int fd, pid_t pgrp)
3691{
3692 if (tcsetpgrp(fd, pgrp))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00003693 ash_msg_and_raise_error("can't set tty process group (%m)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003694}
3695
3696/*
3697 * Turn job control on and off.
3698 *
3699 * Note: This code assumes that the third arg to ioctl is a character
3700 * pointer, which is true on Berkeley systems but not System V. Since
3701 * System V doesn't have job control yet, this isn't a problem now.
3702 *
3703 * Called with interrupts off.
3704 */
3705static void
3706setjobctl(int on)
3707{
3708 int fd;
3709 int pgrp;
3710
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003711 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003712 return;
3713 if (on) {
3714 int ofd;
3715 ofd = fd = open(_PATH_TTY, O_RDWR);
3716 if (fd < 0) {
3717 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
3718 * That sometimes helps to acquire controlling tty.
3719 * Obviously, a workaround for bugs when someone
3720 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003721 fd = 2;
3722 while (!isatty(fd))
3723 if (--fd < 0)
3724 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003725 }
3726 fd = fcntl(fd, F_DUPFD, 10);
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003727 if (ofd >= 0)
3728 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003729 if (fd < 0)
3730 goto out;
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003731 /* fd is a tty at this point */
Denis Vlasenko96e1b382007-09-30 23:50:48 +00003732 close_on_exec_on(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003733 do { /* while we are in the background */
3734 pgrp = tcgetpgrp(fd);
3735 if (pgrp < 0) {
3736 out:
3737 ash_msg("can't access tty; job control turned off");
3738 mflag = on = 0;
3739 goto close;
3740 }
3741 if (pgrp == getpgrp())
3742 break;
3743 killpg(0, SIGTTIN);
3744 } while (1);
3745 initialpgrp = pgrp;
3746
3747 setsignal(SIGTSTP);
3748 setsignal(SIGTTOU);
3749 setsignal(SIGTTIN);
3750 pgrp = rootpid;
3751 setpgid(0, pgrp);
3752 xtcsetpgrp(fd, pgrp);
3753 } else {
3754 /* turning job control off */
3755 fd = ttyfd;
3756 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003757 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003758 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00003759 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003760 setpgid(0, pgrp);
3761 setsignal(SIGTSTP);
3762 setsignal(SIGTTOU);
3763 setsignal(SIGTTIN);
3764 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00003765 if (fd >= 0)
3766 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003767 fd = -1;
3768 }
3769 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003770 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003771}
3772
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003773static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003774killcmd(int argc, char **argv)
3775{
Denis Vlasenko68404f12008-03-17 09:00:54 +00003776 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003777 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003778 do {
3779 if (argv[i][0] == '%') {
3780 struct job *jp = getjob(argv[i], 0);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003781 unsigned pid = jp->ps[0].ps_pid;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003782 /* Enough space for ' -NNN<nul>' */
3783 argv[i] = alloca(sizeof(int)*3 + 3);
3784 /* kill_main has matching code to expect
3785 * leading space. Needed to not confuse
3786 * negative pids with "kill -SIGNAL_NO" syntax */
3787 sprintf(argv[i], " -%u", pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003789 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003791 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792}
3793
3794static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01003795showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796{
Denys Vlasenko285ad152009-12-04 23:02:27 +01003797 struct procstat *ps;
3798 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003799
Denys Vlasenko285ad152009-12-04 23:02:27 +01003800 psend = jp->ps + jp->nprocs;
3801 for (ps = jp->ps + 1; ps < psend; ps++)
3802 printf(" | %s", ps->ps_cmd);
3803 outcslow('\n', stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003804 flush_stdout_stderr();
3805}
3806
3807
3808static int
3809restartjob(struct job *jp, int mode)
3810{
3811 struct procstat *ps;
3812 int i;
3813 int status;
3814 pid_t pgid;
3815
3816 INT_OFF;
3817 if (jp->state == JOBDONE)
3818 goto out;
3819 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003820 pgid = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003821 if (mode == FORK_FG)
3822 xtcsetpgrp(ttyfd, pgid);
3823 killpg(pgid, SIGCONT);
3824 ps = jp->ps;
3825 i = jp->nprocs;
3826 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003827 if (WIFSTOPPED(ps->ps_status)) {
3828 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003829 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00003830 ps++;
3831 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003832 out:
3833 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
3834 INT_ON;
3835 return status;
3836}
3837
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003838static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003839fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840{
3841 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842 int mode;
3843 int retval;
3844
3845 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
3846 nextopt(nullstr);
3847 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003848 do {
3849 jp = getjob(*argv, 1);
3850 if (mode == FORK_BG) {
3851 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01003852 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003853 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003854 out1str(jp->ps[0].ps_cmd);
3855 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003856 retval = restartjob(jp, mode);
3857 } while (*argv && *++argv);
3858 return retval;
3859}
3860#endif
3861
3862static int
3863sprint_status(char *s, int status, int sigonly)
3864{
3865 int col;
3866 int st;
3867
3868 col = 0;
3869 if (!WIFEXITED(status)) {
3870#if JOBS
3871 if (WIFSTOPPED(status))
3872 st = WSTOPSIG(status);
3873 else
3874#endif
3875 st = WTERMSIG(status);
3876 if (sigonly) {
3877 if (st == SIGINT || st == SIGPIPE)
3878 goto out;
3879#if JOBS
3880 if (WIFSTOPPED(status))
3881 goto out;
3882#endif
3883 }
3884 st &= 0x7f;
3885 col = fmtstr(s, 32, strsignal(st));
3886 if (WCOREDUMP(status)) {
3887 col += fmtstr(s + col, 16, " (core dumped)");
3888 }
3889 } else if (!sigonly) {
3890 st = WEXITSTATUS(status);
3891 if (st)
3892 col = fmtstr(s, 16, "Done(%d)", st);
3893 else
3894 col = fmtstr(s, 16, "Done");
3895 }
3896 out:
3897 return col;
3898}
3899
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003900static int
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003901dowait(int wait_flags, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003902{
3903 int pid;
3904 int status;
3905 struct job *jp;
3906 struct job *thisjob;
3907 int state;
3908
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00003909 TRACE(("dowait(0x%x) called\n", wait_flags));
3910
3911 /* Do a wait system call. If job control is compiled in, we accept
3912 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3913 * NB: _not_ safe_waitpid, we need to detect EINTR */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003914 if (doing_jobctl)
3915 wait_flags |= WUNTRACED;
3916 pid = waitpid(-1, &status, wait_flags);
Denis Vlasenkob21f3792009-03-19 23:09:58 +00003917 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
3918 pid, status, errno, strerror(errno)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003919 if (pid <= 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003920 return pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003921
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003922 INT_OFF;
3923 thisjob = NULL;
3924 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003925 struct procstat *ps;
3926 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003927 if (jp->state == JOBDONE)
3928 continue;
3929 state = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003930 ps = jp->ps;
3931 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003933 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003934 TRACE(("Job %d: changing status of proc %d "
3935 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01003936 jobno(jp), pid, ps->ps_status, status));
3937 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 thisjob = jp;
3939 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01003940 if (ps->ps_status == -1)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003941 state = JOBRUNNING;
3942#if JOBS
3943 if (state == JOBRUNNING)
3944 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01003945 if (WIFSTOPPED(ps->ps_status)) {
3946 jp->stopstatus = ps->ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003947 state = JOBSTOPPED;
3948 }
3949#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01003950 } while (++ps < psend);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003951 if (thisjob)
3952 goto gotjob;
3953 }
3954#if JOBS
3955 if (!WIFSTOPPED(status))
3956#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003957 jobless--;
3958 goto out;
3959
3960 gotjob:
3961 if (state != JOBRUNNING) {
3962 thisjob->changed = 1;
3963
3964 if (thisjob->state != state) {
3965 TRACE(("Job %d: changing state from %d to %d\n",
3966 jobno(thisjob), thisjob->state, state));
3967 thisjob->state = state;
3968#if JOBS
3969 if (state == JOBSTOPPED) {
3970 set_curjob(thisjob, CUR_STOPPED);
3971 }
3972#endif
3973 }
3974 }
3975
3976 out:
3977 INT_ON;
3978
3979 if (thisjob && thisjob == job) {
3980 char s[48 + 1];
3981 int len;
3982
3983 len = sprint_status(s, status, 1);
3984 if (len) {
3985 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00003986 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003987 out2str(s);
3988 }
3989 }
3990 return pid;
3991}
3992
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003993static int
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003994blocking_wait_with_raise_on_sig(void)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003995{
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02003996 pid_t pid = dowait(DOWAIT_BLOCK, NULL);
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003997 if (pid <= 0 && pending_sig)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003998 raise_exception(EXSIG);
3999 return pid;
4000}
4001
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004002#if JOBS
4003static void
4004showjob(FILE *out, struct job *jp, int mode)
4005{
4006 struct procstat *ps;
4007 struct procstat *psend;
4008 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004009 int indent_col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004010 char s[80];
4011
4012 ps = jp->ps;
4013
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004014 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004015 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004016 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004017 return;
4018 }
4019
4020 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004021 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004022
4023 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004024 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004025 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004026 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004027
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004028 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004029 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004030
4031 psend = ps + jp->nprocs;
4032
4033 if (jp->state == JOBRUNNING) {
4034 strcpy(s + col, "Running");
4035 col += sizeof("Running") - 1;
4036 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004037 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004038 if (jp->state == JOBSTOPPED)
4039 status = jp->stopstatus;
4040 col += sprint_status(s + col, status, 0);
4041 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004042 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004044 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4045 * or prints several "PID | <cmdN>" lines,
4046 * depending on SHOW_PIDS bit.
4047 * We do not print status of individual processes
4048 * between PID and <cmdN>. bash does it, but not very well:
4049 * first line shows overall job status, not process status,
4050 * making it impossible to know 1st process status.
4051 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004052 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004053 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004054 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004055 s[0] = '\0';
4056 col = 33;
4057 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004058 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004059 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004060 fprintf(out, "%s%*c%s%s",
4061 s,
4062 33 - col >= 0 ? 33 - col : 0, ' ',
4063 ps == jp->ps ? "" : "| ",
4064 ps->ps_cmd
4065 );
4066 } while (++ps != psend);
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004067 outcslow('\n', out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068
4069 jp->changed = 0;
4070
4071 if (jp->state == JOBDONE) {
4072 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4073 freejob(jp);
4074 }
4075}
4076
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004077/*
4078 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4079 * statuses have changed since the last call to showjobs.
4080 */
4081static void
4082showjobs(FILE *out, int mode)
4083{
4084 struct job *jp;
4085
Denys Vlasenko883cea42009-07-11 15:31:59 +02004086 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004087
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004088 /* Handle all finished jobs */
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004089 while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004090 continue;
4091
4092 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004093 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004094 showjob(out, jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004095 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 }
4097}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004098
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004099static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004100jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004101{
4102 int mode, m;
4103
4104 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004105 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004106 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004107 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004108 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004109 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004110 }
4111
4112 argv = argptr;
4113 if (*argv) {
4114 do
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004115 showjob(stdout, getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004116 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004117 } else {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004118 showjobs(stdout, mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004119 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004120
4121 return 0;
4122}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004123#endif /* JOBS */
4124
Michael Abbott359da5e2009-12-04 23:03:29 +01004125/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004126static int
4127getstatus(struct job *job)
4128{
4129 int status;
4130 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004131 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004132
Michael Abbott359da5e2009-12-04 23:03:29 +01004133 /* Fetch last member's status */
4134 ps = job->ps + job->nprocs - 1;
4135 status = ps->ps_status;
4136 if (pipefail) {
4137 /* "set -o pipefail" mode: use last _nonzero_ status */
4138 while (status == 0 && --ps >= job->ps)
4139 status = ps->ps_status;
4140 }
4141
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004142 retval = WEXITSTATUS(status);
4143 if (!WIFEXITED(status)) {
4144#if JOBS
4145 retval = WSTOPSIG(status);
4146 if (!WIFSTOPPED(status))
4147#endif
4148 {
4149 /* XXX: limits number of signals */
4150 retval = WTERMSIG(status);
4151#if JOBS
4152 if (retval == SIGINT)
4153 job->sigint = 1;
4154#endif
4155 }
4156 retval += 128;
4157 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004158 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004159 jobno(job), job->nprocs, status, retval));
4160 return retval;
4161}
4162
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004163static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004164waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004165{
4166 struct job *job;
4167 int retval;
4168 struct job *jp;
4169
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004170 if (pending_sig)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004171 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172
4173 nextopt(nullstr);
4174 retval = 0;
4175
4176 argv = argptr;
4177 if (!*argv) {
4178 /* wait for all jobs */
4179 for (;;) {
4180 jp = curjob;
4181 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004182 if (!jp) /* no running procs */
4183 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 if (jp->state == JOBRUNNING)
4185 break;
4186 jp->waited = 1;
4187 jp = jp->prev_job;
4188 }
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004189 blocking_wait_with_raise_on_sig();
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004190 /* man bash:
4191 * "When bash is waiting for an asynchronous command via
4192 * the wait builtin, the reception of a signal for which a trap
4193 * has been set will cause the wait builtin to return immediately
4194 * with an exit status greater than 128, immediately after which
4195 * the trap is executed."
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004196 *
4197 * blocking_wait_with_raise_on_sig raises signal handlers
4198 * if it gets no pid (pid < 0). However,
4199 * if child sends us a signal *and immediately exits*,
4200 * blocking_wait_with_raise_on_sig gets pid > 0
4201 * and does not handle pending_sig. Check this case: */
4202 if (pending_sig)
4203 raise_exception(EXSIG);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204 }
4205 }
4206
4207 retval = 127;
4208 do {
4209 if (**argv != '%') {
4210 pid_t pid = number(*argv);
4211 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004212 while (1) {
4213 if (!job)
4214 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004215 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004216 break;
4217 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004218 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004219 } else
4220 job = getjob(*argv, 0);
4221 /* loop until process terminated or stopped */
4222 while (job->state == JOBRUNNING)
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004223 blocking_wait_with_raise_on_sig();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004224 job->waited = 1;
4225 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004226 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004227 } while (*++argv);
4228
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004229 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004230 return retval;
4231}
4232
4233static struct job *
4234growjobtab(void)
4235{
4236 size_t len;
4237 ptrdiff_t offset;
4238 struct job *jp, *jq;
4239
4240 len = njobs * sizeof(*jp);
4241 jq = jobtab;
4242 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4243
4244 offset = (char *)jp - (char *)jq;
4245 if (offset) {
4246 /* Relocate pointers */
4247 size_t l = len;
4248
4249 jq = (struct job *)((char *)jq + l);
4250 while (l) {
4251 l -= sizeof(*jp);
4252 jq--;
4253#define joff(p) ((struct job *)((char *)(p) + l))
4254#define jmove(p) (p) = (void *)((char *)(p) + offset)
4255 if (joff(jp)->ps == &jq->ps0)
4256 jmove(joff(jp)->ps);
4257 if (joff(jp)->prev_job)
4258 jmove(joff(jp)->prev_job);
4259 }
4260 if (curjob)
4261 jmove(curjob);
4262#undef joff
4263#undef jmove
4264 }
4265
4266 njobs += 4;
4267 jobtab = jp;
4268 jp = (struct job *)((char *)jp + len);
4269 jq = jp + 3;
4270 do {
4271 jq->used = 0;
4272 } while (--jq >= jp);
4273 return jp;
4274}
4275
4276/*
4277 * Return a new job structure.
4278 * Called with interrupts off.
4279 */
4280static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004281makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004282{
4283 int i;
4284 struct job *jp;
4285
4286 for (i = njobs, jp = jobtab; ; jp++) {
4287 if (--i < 0) {
4288 jp = growjobtab();
4289 break;
4290 }
4291 if (jp->used == 0)
4292 break;
4293 if (jp->state != JOBDONE || !jp->waited)
4294 continue;
4295#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004296 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004297 continue;
4298#endif
4299 freejob(jp);
4300 break;
4301 }
4302 memset(jp, 0, sizeof(*jp));
4303#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004304 /* jp->jobctl is a bitfield.
4305 * "jp->jobctl |= jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004306 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004307 jp->jobctl = 1;
4308#endif
4309 jp->prev_job = curjob;
4310 curjob = jp;
4311 jp->used = 1;
4312 jp->ps = &jp->ps0;
4313 if (nprocs > 1) {
4314 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4315 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004316 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004317 jobno(jp)));
4318 return jp;
4319}
4320
4321#if JOBS
4322/*
4323 * Return a string identifying a command (to be printed by the
4324 * jobs command).
4325 */
4326static char *cmdnextc;
4327
4328static void
4329cmdputs(const char *s)
4330{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004331 static const char vstype[VSTYPE + 1][3] = {
4332 "", "}", "-", "+", "?", "=",
4333 "%", "%%", "#", "##"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00004334 IF_ASH_BASH_COMPAT(, ":", "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004335 };
4336
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004337 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004338 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004339 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004340 unsigned char c;
4341 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004342 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004343
Denys Vlasenko46a14772009-12-10 21:27:13 +01004344 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004345 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4346 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004347 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004348 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004349 switch (c) {
4350 case CTLESC:
4351 c = *p++;
4352 break;
4353 case CTLVAR:
4354 subtype = *p++;
4355 if ((subtype & VSTYPE) == VSLENGTH)
4356 str = "${#";
4357 else
4358 str = "${";
4359 if (!(subtype & VSQUOTE) == !(quoted & 1))
4360 goto dostr;
4361 quoted ^= 1;
4362 c = '"';
4363 break;
4364 case CTLENDVAR:
4365 str = "\"}" + !(quoted & 1);
4366 quoted >>= 1;
4367 subtype = 0;
4368 goto dostr;
4369 case CTLBACKQ:
4370 str = "$(...)";
4371 goto dostr;
4372 case CTLBACKQ+CTLQUOTE:
4373 str = "\"$(...)\"";
4374 goto dostr;
Mike Frysinger98c52642009-04-02 10:02:37 +00004375#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004376 case CTLARI:
4377 str = "$((";
4378 goto dostr;
4379 case CTLENDARI:
4380 str = "))";
4381 goto dostr;
4382#endif
4383 case CTLQUOTEMARK:
4384 quoted ^= 1;
4385 c = '"';
4386 break;
4387 case '=':
4388 if (subtype == 0)
4389 break;
4390 if ((subtype & VSTYPE) != VSNORMAL)
4391 quoted <<= 1;
4392 str = vstype[subtype & VSTYPE];
4393 if (subtype & VSNUL)
4394 c = ':';
4395 else
4396 goto checkstr;
4397 break;
4398 case '\'':
4399 case '\\':
4400 case '"':
4401 case '$':
4402 /* These can only happen inside quotes */
4403 cc[0] = c;
4404 str = cc;
4405 c = '\\';
4406 break;
4407 default:
4408 break;
4409 }
4410 USTPUTC(c, nextc);
4411 checkstr:
4412 if (!str)
4413 continue;
4414 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004415 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004416 USTPUTC(c, nextc);
4417 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004418 } /* while *p++ not NUL */
4419
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004420 if (quoted & 1) {
4421 USTPUTC('"', nextc);
4422 }
4423 *nextc = 0;
4424 cmdnextc = nextc;
4425}
4426
4427/* cmdtxt() and cmdlist() call each other */
4428static void cmdtxt(union node *n);
4429
4430static void
4431cmdlist(union node *np, int sep)
4432{
4433 for (; np; np = np->narg.next) {
4434 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004435 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004436 cmdtxt(np);
4437 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004438 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004439 }
4440}
4441
4442static void
4443cmdtxt(union node *n)
4444{
4445 union node *np;
4446 struct nodelist *lp;
4447 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004448
4449 if (!n)
4450 return;
4451 switch (n->type) {
4452 default:
4453#if DEBUG
4454 abort();
4455#endif
4456 case NPIPE:
4457 lp = n->npipe.cmdlist;
4458 for (;;) {
4459 cmdtxt(lp->n);
4460 lp = lp->next;
4461 if (!lp)
4462 break;
4463 cmdputs(" | ");
4464 }
4465 break;
4466 case NSEMI:
4467 p = "; ";
4468 goto binop;
4469 case NAND:
4470 p = " && ";
4471 goto binop;
4472 case NOR:
4473 p = " || ";
4474 binop:
4475 cmdtxt(n->nbinary.ch1);
4476 cmdputs(p);
4477 n = n->nbinary.ch2;
4478 goto donode;
4479 case NREDIR:
4480 case NBACKGND:
4481 n = n->nredir.n;
4482 goto donode;
4483 case NNOT:
4484 cmdputs("!");
4485 n = n->nnot.com;
4486 donode:
4487 cmdtxt(n);
4488 break;
4489 case NIF:
4490 cmdputs("if ");
4491 cmdtxt(n->nif.test);
4492 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004494 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495 cmdputs("; else ");
4496 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004497 } else {
4498 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004499 }
4500 p = "; fi";
4501 goto dotail;
4502 case NSUBSHELL:
4503 cmdputs("(");
4504 n = n->nredir.n;
4505 p = ")";
4506 goto dotail;
4507 case NWHILE:
4508 p = "while ";
4509 goto until;
4510 case NUNTIL:
4511 p = "until ";
4512 until:
4513 cmdputs(p);
4514 cmdtxt(n->nbinary.ch1);
4515 n = n->nbinary.ch2;
4516 p = "; done";
4517 dodo:
4518 cmdputs("; do ");
4519 dotail:
4520 cmdtxt(n);
4521 goto dotail2;
4522 case NFOR:
4523 cmdputs("for ");
4524 cmdputs(n->nfor.var);
4525 cmdputs(" in ");
4526 cmdlist(n->nfor.args, 1);
4527 n = n->nfor.body;
4528 p = "; done";
4529 goto dodo;
4530 case NDEFUN:
4531 cmdputs(n->narg.text);
4532 p = "() { ... }";
4533 goto dotail2;
4534 case NCMD:
4535 cmdlist(n->ncmd.args, 1);
4536 cmdlist(n->ncmd.redirect, 0);
4537 break;
4538 case NARG:
4539 p = n->narg.text;
4540 dotail2:
4541 cmdputs(p);
4542 break;
4543 case NHERE:
4544 case NXHERE:
4545 p = "<<...";
4546 goto dotail2;
4547 case NCASE:
4548 cmdputs("case ");
4549 cmdputs(n->ncase.expr->narg.text);
4550 cmdputs(" in ");
4551 for (np = n->ncase.cases; np; np = np->nclist.next) {
4552 cmdtxt(np->nclist.pattern);
4553 cmdputs(") ");
4554 cmdtxt(np->nclist.body);
4555 cmdputs(";; ");
4556 }
4557 p = "esac";
4558 goto dotail2;
4559 case NTO:
4560 p = ">";
4561 goto redir;
4562 case NCLOBBER:
4563 p = ">|";
4564 goto redir;
4565 case NAPPEND:
4566 p = ">>";
4567 goto redir;
Denis Vlasenko559691a2008-10-05 18:39:31 +00004568#if ENABLE_ASH_BASH_COMPAT
4569 case NTO2:
4570#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004571 case NTOFD:
4572 p = ">&";
4573 goto redir;
4574 case NFROM:
4575 p = "<";
4576 goto redir;
4577 case NFROMFD:
4578 p = "<&";
4579 goto redir;
4580 case NFROMTO:
4581 p = "<>";
4582 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004583 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004584 cmdputs(p);
4585 if (n->type == NTOFD || n->type == NFROMFD) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00004586 cmdputs(utoa(n->ndup.dupfd));
4587 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004588 }
4589 n = n->nfile.fname;
4590 goto donode;
4591 }
4592}
4593
4594static char *
4595commandtext(union node *n)
4596{
4597 char *name;
4598
4599 STARTSTACKSTR(cmdnextc);
4600 cmdtxt(n);
4601 name = stackblock();
4602 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
4603 name, cmdnextc, cmdnextc));
4604 return ckstrdup(name);
4605}
4606#endif /* JOBS */
4607
4608/*
4609 * Fork off a subshell. If we are doing job control, give the subshell its
4610 * own process group. Jp is a job structure that the job is to be added to.
4611 * N is the command that will be evaluated by the child. Both jp and n may
4612 * be NULL. The mode parameter can be one of the following:
4613 * FORK_FG - Fork off a foreground process.
4614 * FORK_BG - Fork off a background process.
4615 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
4616 * process group even if job control is on.
4617 *
4618 * When job control is turned off, background processes have their standard
4619 * input redirected to /dev/null (except for the second and later processes
4620 * in a pipeline).
4621 *
4622 * Called with interrupts off.
4623 */
4624/*
4625 * Clear traps on a fork.
4626 */
4627static void
4628clear_traps(void)
4629{
4630 char **tp;
4631
4632 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004633 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004634 INT_OFF;
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004635 if (trap_ptr == trap)
4636 free(*tp);
4637 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004638 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02004639 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004640 setsignal(tp - trap);
4641 INT_ON;
4642 }
4643 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02004644 may_have_traps = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004645}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004646
4647/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004648static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00004649
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004650/* Called after fork(), in child */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004651static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004652forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004653{
4654 int oldlvl;
4655
4656 TRACE(("Child shell %d\n", getpid()));
4657 oldlvl = shlvl;
4658 shlvl++;
4659
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004660 /* man bash: "Non-builtin commands run by bash have signal handlers
4661 * set to the values inherited by the shell from its parent".
4662 * Do we do it correctly? */
4663
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02004665
4666 if (mode == FORK_NOJOB /* is it `xxx` ? */
4667 && n && n->type == NCMD /* is it single cmd? */
4668 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01004669 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004670 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
4671 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
4672 ) {
4673 TRACE(("Trap hack\n"));
4674 /* Awful hack for `trap` or $(trap).
4675 *
4676 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
4677 * contains an example where "trap" is executed in a subshell:
4678 *
4679 * save_traps=$(trap)
4680 * ...
4681 * eval "$save_traps"
4682 *
4683 * Standard does not say that "trap" in subshell shall print
4684 * parent shell's traps. It only says that its output
4685 * must have suitable form, but then, in the above example
4686 * (which is not supposed to be normative), it implies that.
4687 *
4688 * bash (and probably other shell) does implement it
4689 * (traps are reset to defaults, but "trap" still shows them),
4690 * but as a result, "trap" logic is hopelessly messed up:
4691 *
4692 * # trap
4693 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
4694 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
4695 * # true | trap <--- trap is in subshell - no output (ditto)
4696 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
4697 * trap -- 'echo Ho' SIGWINCH
4698 * # echo `(trap)` <--- in subshell in subshell - output
4699 * trap -- 'echo Ho' SIGWINCH
4700 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
4701 * trap -- 'echo Ho' SIGWINCH
4702 *
4703 * The rules when to forget and when to not forget traps
4704 * get really complex and nonsensical.
4705 *
4706 * Our solution: ONLY bare $(trap) or `trap` is special.
4707 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004708 /* Save trap handler strings for trap builtin to print */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02004709 trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02004710 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02004711 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02004712 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004713#if JOBS
4714 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004715 doing_jobctl = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004716 if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
4717 pid_t pgrp;
4718
4719 if (jp->nprocs == 0)
4720 pgrp = getpid();
4721 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004722 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004723 /* this can fail because we are doing it in the parent also */
4724 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004725 if (mode == FORK_FG)
4726 xtcsetpgrp(ttyfd, pgrp);
4727 setsignal(SIGTSTP);
4728 setsignal(SIGTTOU);
4729 } else
4730#endif
4731 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004732 /* man bash: "When job control is not in effect,
4733 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004734 ignoresig(SIGINT);
4735 ignoresig(SIGQUIT);
4736 if (jp->nprocs == 0) {
4737 close(0);
4738 if (open(bb_dev_null, O_RDONLY) != 0)
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00004739 ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004740 }
4741 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004742 if (!oldlvl) {
4743 if (iflag) { /* why if iflag only? */
4744 setsignal(SIGINT);
4745 setsignal(SIGTERM);
4746 }
4747 /* man bash:
4748 * "In all cases, bash ignores SIGQUIT. Non-builtin
4749 * commands run by bash have signal handlers
4750 * set to the values inherited by the shell
4751 * from its parent".
4752 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004753 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004754 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004755#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02004756 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01004757 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02004758 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004759 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02004760 /* "jobs": we do not want to clear job list for it,
4761 * instead we remove only _its_ own_ job from job list.
4762 * This makes "jobs .... | cat" more useful.
4763 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004764 freejob(curjob);
4765 return;
4766 }
4767#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004768 for (jp = curjob; jp; jp = jp->prev_job)
4769 freejob(jp);
4770 jobless = 0;
4771}
4772
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00004773/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00004774#if !JOBS
4775#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
4776#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004777static void
4778forkparent(struct job *jp, union node *n, int mode, pid_t pid)
4779{
4780 TRACE(("In parent shell: child = %d\n", pid));
4781 if (!jp) {
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004782 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
4783 continue;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004784 jobless++;
4785 return;
4786 }
4787#if JOBS
4788 if (mode != FORK_NOJOB && jp->jobctl) {
4789 int pgrp;
4790
4791 if (jp->nprocs == 0)
4792 pgrp = pid;
4793 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01004794 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004795 /* This can fail because we are doing it in the child also */
4796 setpgid(pid, pgrp);
4797 }
4798#endif
4799 if (mode == FORK_BG) {
4800 backgndpid = pid; /* set $! */
4801 set_curjob(jp, CUR_RUNNING);
4802 }
4803 if (jp) {
4804 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01004805 ps->ps_pid = pid;
4806 ps->ps_status = -1;
4807 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004808#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004809 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004810 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004811#endif
4812 }
4813}
4814
4815static int
4816forkshell(struct job *jp, union node *n, int mode)
4817{
4818 int pid;
4819
4820 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
4821 pid = fork();
4822 if (pid < 0) {
4823 TRACE(("Fork failed, errno=%d", errno));
4824 if (jp)
4825 freejob(jp);
Denis Vlasenkofa0b56d2008-07-01 16:09:07 +00004826 ash_msg_and_raise_error("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004827 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02004828 if (pid == 0) {
4829 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02004830 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004831 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004832 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02004833 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004834 return pid;
4835}
4836
4837/*
4838 * Wait for job to finish.
4839 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004840 * Under job control we have the problem that while a child process
4841 * is running interrupts generated by the user are sent to the child
4842 * but not to the shell. This means that an infinite loop started by
4843 * an interactive user may be hard to kill. With job control turned off,
4844 * an interactive user may place an interactive program inside a loop.
4845 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004846 * these interrupts to also abort the loop. The approach we take here
4847 * is to have the shell ignore interrupt signals while waiting for a
4848 * foreground process to terminate, and then send itself an interrupt
4849 * signal if the child process was terminated by an interrupt signal.
4850 * Unfortunately, some programs want to do a bit of cleanup and then
4851 * exit on interrupt; unless these processes terminate themselves by
4852 * sending a signal to themselves (instead of calling exit) they will
4853 * confuse this approach.
4854 *
4855 * Called with interrupts off.
4856 */
4857static int
4858waitforjob(struct job *jp)
4859{
4860 int st;
4861
4862 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004863
4864 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004865 while (jp->state == JOBRUNNING) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004866 /* In non-interactive shells, we _can_ get
4867 * a keyboard signal here and be EINTRed,
4868 * but we just loop back, waiting for command to complete.
4869 *
4870 * man bash:
4871 * "If bash is waiting for a command to complete and receives
4872 * a signal for which a trap has been set, the trap
4873 * will not be executed until the command completes."
4874 *
4875 * Reality is that even if trap is not set, bash
4876 * will not act on the signal until command completes.
4877 * Try this. sleep5intoff.c:
4878 * #include <signal.h>
4879 * #include <unistd.h>
4880 * int main() {
4881 * sigset_t set;
4882 * sigemptyset(&set);
4883 * sigaddset(&set, SIGINT);
4884 * sigaddset(&set, SIGQUIT);
4885 * sigprocmask(SIG_BLOCK, &set, NULL);
4886 * sleep(5);
4887 * return 0;
4888 * }
4889 * $ bash -c './sleep5intoff; echo hi'
4890 * ^C^C^C^C <--- pressing ^C once a second
4891 * $ _
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004892 * $ bash -c './sleep5intoff; echo hi'
4893 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
4894 * $ _
4895 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004896 dowait(DOWAIT_BLOCK, jp);
4897 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004898 INT_ON;
4899
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004900 st = getstatus(jp);
4901#if JOBS
4902 if (jp->jobctl) {
4903 xtcsetpgrp(ttyfd, rootpid);
4904 /*
4905 * This is truly gross.
4906 * If we're doing job control, then we did a TIOCSPGRP which
4907 * caused us (the shell) to no longer be in the controlling
4908 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
4909 * intuit from the subprocess exit status whether a SIGINT
4910 * occurred, and if so interrupt ourselves. Yuck. - mycroft
4911 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004912 if (jp->sigint) /* TODO: do the same with all signals */
4913 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004914 }
4915 if (jp->state == JOBDONE)
4916#endif
4917 freejob(jp);
4918 return st;
4919}
4920
4921/*
4922 * return 1 if there are stopped jobs, otherwise 0
4923 */
4924static int
4925stoppedjobs(void)
4926{
4927 struct job *jp;
4928 int retval;
4929
4930 retval = 0;
4931 if (job_warning)
4932 goto out;
4933 jp = curjob;
4934 if (jp && jp->state == JOBSTOPPED) {
4935 out2str("You have stopped jobs.\n");
4936 job_warning = 2;
4937 retval++;
4938 }
4939 out:
4940 return retval;
4941}
4942
4943
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004944/* ============ redir.c
4945 *
4946 * Code for dealing with input/output redirection.
4947 */
4948
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01004949#undef EMPTY
4950#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004951#define EMPTY -2 /* marks an unused slot in redirtab */
Denis Vlasenko7d75a962007-11-22 08:16:57 +00004952#define CLOSED -3 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00004953
4954/*
4955 * Open a file in noclobber mode.
4956 * The code was copied from bash.
4957 */
4958static int
4959noclobberopen(const char *fname)
4960{
4961 int r, fd;
4962 struct stat finfo, finfo2;
4963
4964 /*
4965 * If the file exists and is a regular file, return an error
4966 * immediately.
4967 */
4968 r = stat(fname, &finfo);
4969 if (r == 0 && S_ISREG(finfo.st_mode)) {
4970 errno = EEXIST;
4971 return -1;
4972 }
4973
4974 /*
4975 * If the file was not present (r != 0), make sure we open it
4976 * exclusively so that if it is created before we open it, our open
4977 * will fail. Make sure that we do not truncate an existing file.
4978 * Note that we don't turn on O_EXCL unless the stat failed -- if the
4979 * file was not a regular file, we leave O_EXCL off.
4980 */
4981 if (r != 0)
4982 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
4983 fd = open(fname, O_WRONLY|O_CREAT, 0666);
4984
4985 /* If the open failed, return the file descriptor right away. */
4986 if (fd < 0)
4987 return fd;
4988
4989 /*
4990 * OK, the open succeeded, but the file may have been changed from a
4991 * non-regular file to a regular file between the stat and the open.
4992 * We are assuming that the O_EXCL open handles the case where FILENAME
4993 * did not exist and is symlinked to an existing file between the stat
4994 * and open.
4995 */
4996
4997 /*
4998 * If we can open it and fstat the file descriptor, and neither check
4999 * revealed that it was a regular file, and the file has not been
5000 * replaced, return the file descriptor.
5001 */
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005002 if (fstat(fd, &finfo2) == 0
5003 && !S_ISREG(finfo2.st_mode)
5004 && finfo.st_dev == finfo2.st_dev
5005 && finfo.st_ino == finfo2.st_ino
5006 ) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005007 return fd;
Denys Vlasenko8d3e2252010-08-31 12:42:06 +02005008 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005009
5010 /* The file has been replaced. badness. */
5011 close(fd);
5012 errno = EEXIST;
5013 return -1;
5014}
5015
5016/*
5017 * Handle here documents. Normally we fork off a process to write the
5018 * data to a pipe. If the document is short, we can stuff the data in
5019 * the pipe without forking.
5020 */
5021/* openhere needs this forward reference */
5022static void expandhere(union node *arg, int fd);
5023static int
5024openhere(union node *redir)
5025{
5026 int pip[2];
5027 size_t len = 0;
5028
5029 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005030 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005031 if (redir->type == NHERE) {
5032 len = strlen(redir->nhere.doc->narg.text);
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005033 if (len <= PIPE_BUF) {
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005034 full_write(pip[1], redir->nhere.doc->narg.text, len);
5035 goto out;
5036 }
5037 }
5038 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005039 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005040 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005041 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5042 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5043 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5044 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005045 signal(SIGPIPE, SIG_DFL);
5046 if (redir->type == NHERE)
5047 full_write(pip[1], redir->nhere.doc->narg.text, len);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005048 else /* NXHERE */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005049 expandhere(redir->nhere.doc, pip[1]);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005050 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005051 }
5052 out:
5053 close(pip[1]);
5054 return pip[0];
5055}
5056
5057static int
5058openredirect(union node *redir)
5059{
5060 char *fname;
5061 int f;
5062
5063 switch (redir->nfile.type) {
5064 case NFROM:
5065 fname = redir->nfile.expfname;
5066 f = open(fname, O_RDONLY);
5067 if (f < 0)
5068 goto eopen;
5069 break;
5070 case NFROMTO:
5071 fname = redir->nfile.expfname;
Andreas Bühmannda75f442010-06-24 04:32:37 +02005072 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005073 if (f < 0)
5074 goto ecreate;
5075 break;
5076 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00005077#if ENABLE_ASH_BASH_COMPAT
5078 case NTO2:
5079#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005080 /* Take care of noclobber mode. */
5081 if (Cflag) {
5082 fname = redir->nfile.expfname;
5083 f = noclobberopen(fname);
5084 if (f < 0)
5085 goto ecreate;
5086 break;
5087 }
5088 /* FALLTHROUGH */
5089 case NCLOBBER:
5090 fname = redir->nfile.expfname;
5091 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5092 if (f < 0)
5093 goto ecreate;
5094 break;
5095 case NAPPEND:
5096 fname = redir->nfile.expfname;
5097 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5098 if (f < 0)
5099 goto ecreate;
5100 break;
5101 default:
5102#if DEBUG
5103 abort();
5104#endif
5105 /* Fall through to eliminate warning. */
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005106/* Our single caller does this itself */
Denis Vlasenko0b769642008-07-24 07:54:57 +00005107// case NTOFD:
5108// case NFROMFD:
5109// f = -1;
5110// break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005111 case NHERE:
5112 case NXHERE:
5113 f = openhere(redir);
5114 break;
5115 }
5116
5117 return f;
5118 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005119 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005120 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005121 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005122}
5123
5124/*
5125 * Copy a file descriptor to be >= to. Returns -1
5126 * if the source file descriptor is closed, EMPTY if there are no unused
5127 * file descriptors left.
5128 */
Denis Vlasenko5a867312008-07-24 19:46:38 +00005129/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
5130 * old code was doing close(to) prior to copyfd() to achieve the same */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005131enum {
5132 COPYFD_EXACT = (int)~(INT_MAX),
5133 COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
5134};
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005135static int
5136copyfd(int from, int to)
5137{
5138 int newfd;
5139
Denis Vlasenko5a867312008-07-24 19:46:38 +00005140 if (to & COPYFD_EXACT) {
5141 to &= ~COPYFD_EXACT;
5142 /*if (from != to)*/
5143 newfd = dup2(from, to);
5144 } else {
5145 newfd = fcntl(from, F_DUPFD, to);
5146 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005147 if (newfd < 0) {
5148 if (errno == EMFILE)
5149 return EMPTY;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005150 /* Happens when source fd is not open: try "echo >&99" */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005151 ash_msg_and_raise_error("%d: %m", from);
5152 }
5153 return newfd;
5154}
5155
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005156/* Struct def and variable are moved down to the first usage site */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005157struct two_fd_t {
5158 int orig, copy;
5159};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005160struct redirtab {
5161 struct redirtab *next;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005162 int nullredirs;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005163 int pair_count;
Denys Vlasenko606291b2009-09-23 23:15:43 +02005164 struct two_fd_t two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005165};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005166#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005167
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005168static int need_to_remember(struct redirtab *rp, int fd)
5169{
5170 int i;
5171
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005172 if (!rp) /* remembering was not requested */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005173 return 0;
5174
5175 for (i = 0; i < rp->pair_count; i++) {
5176 if (rp->two_fd[i].orig == fd) {
5177 /* already remembered */
5178 return 0;
5179 }
5180 }
5181 return 1;
5182}
5183
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005184/* "hidden" fd is a fd used to read scripts, or a copy of such */
5185static int is_hidden_fd(struct redirtab *rp, int fd)
5186{
5187 int i;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005188 struct parsefile *pf;
5189
5190 if (fd == -1)
5191 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005192 /* Check open scripts' fds */
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005193 pf = g_parsefile;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005194 while (pf) {
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005195 /* We skip pf_fd == 0 case because of the following case:
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005196 * $ ash # running ash interactively
5197 * $ . ./script.sh
5198 * and in script.sh: "exec 9>&0".
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005199 * Even though top-level pf_fd _is_ 0,
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005200 * it's still ok to use it: "read" builtin uses it,
5201 * why should we cripple "exec" builtin?
5202 */
Denys Vlasenko79b3d422010-06-03 04:29:08 +02005203 if (pf->pf_fd > 0 && fd == pf->pf_fd) {
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005204 return 1;
5205 }
5206 pf = pf->prev;
5207 }
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005208
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005209 if (!rp)
5210 return 0;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005211 /* Check saved fds of redirects */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005212 fd |= COPYFD_RESTORE;
5213 for (i = 0; i < rp->pair_count; i++) {
5214 if (rp->two_fd[i].copy == fd) {
5215 return 1;
5216 }
5217 }
5218 return 0;
5219}
5220
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005221/*
5222 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5223 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005224 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005225 */
5226/* flags passed to redirect */
5227#define REDIR_PUSH 01 /* save previous values of file descriptors */
5228#define REDIR_SAVEFD2 03 /* set preverrout */
5229static void
5230redirect(union node *redir, int flags)
5231{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005232 struct redirtab *sv;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005233 int sv_pos;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005234 int i;
5235 int fd;
5236 int newfd;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005237 int copied_fd2 = -1;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005238
Denis Vlasenko01631112007-12-16 17:20:38 +00005239 g_nullredirs++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005240 if (!redir) {
5241 return;
5242 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005243
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005244 sv = NULL;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005245 sv_pos = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005246 INT_OFF;
5247 if (flags & REDIR_PUSH) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005248 union node *tmp = redir;
5249 do {
5250 sv_pos++;
Denis Vlasenko559691a2008-10-05 18:39:31 +00005251#if ENABLE_ASH_BASH_COMPAT
Chris Metcalfc3c1fb62010-01-08 13:18:06 +01005252 if (tmp->nfile.type == NTO2)
Denis Vlasenko559691a2008-10-05 18:39:31 +00005253 sv_pos++;
5254#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005255 tmp = tmp->nfile.next;
5256 } while (tmp);
5257 sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005258 sv->next = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005259 sv->pair_count = sv_pos;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005260 redirlist = sv;
Denis Vlasenko01631112007-12-16 17:20:38 +00005261 sv->nullredirs = g_nullredirs - 1;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005262 g_nullredirs = 0;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005263 while (sv_pos > 0) {
5264 sv_pos--;
5265 sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
5266 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005267 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005268
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005269 do {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005270 int right_fd = -1;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005271 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005272 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005273 right_fd = redir->ndup.dupfd;
5274 //bb_error_msg("doing %d > %d", fd, right_fd);
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005275 /* redirect from/to same file descriptor? */
5276 if (right_fd == fd)
5277 continue;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005278 /* "echo >&10" and 10 is a fd opened to a sh script? */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005279 if (is_hidden_fd(sv, right_fd)) {
5280 errno = EBADF; /* as if it is closed */
5281 ash_msg_and_raise_error("%d: %m", right_fd);
5282 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005283 newfd = -1;
5284 } else {
5285 newfd = openredirect(redir); /* always >= 0 */
5286 if (fd == newfd) {
5287 /* Descriptor wasn't open before redirect.
5288 * Mark it for close in the future */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005289 if (need_to_remember(sv, fd)) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005290 goto remember_to_close;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005291 }
Denis Vlasenko0b769642008-07-24 07:54:57 +00005292 continue;
5293 }
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005294 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005295#if ENABLE_ASH_BASH_COMPAT
5296 redirect_more:
5297#endif
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005298 if (need_to_remember(sv, fd)) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005299 /* Copy old descriptor */
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005300 /* Careful to not accidentally "save"
5301 * to the same fd as right side fd in N>&M */
5302 int minfd = right_fd < 10 ? 10 : right_fd + 1;
5303 i = fcntl(fd, F_DUPFD, minfd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005304/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
5305 * are closed in popredir() in the child, preventing them from leaking
5306 * into child. (popredir() also cleans up the mess in case of failures)
5307 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005308 if (i == -1) {
5309 i = errno;
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005310 if (i != EBADF) {
5311 /* Strange error (e.g. "too many files" EMFILE?) */
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005312 if (newfd >= 0)
5313 close(newfd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005314 errno = i;
5315 ash_msg_and_raise_error("%d: %m", fd);
5316 /* NOTREACHED */
5317 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005318 /* EBADF: it is not open - good, remember to close it */
5319 remember_to_close:
5320 i = CLOSED;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005321 } else { /* fd is open, save its copy */
5322 /* "exec fd>&-" should not close fds
5323 * which point to script file(s).
5324 * Force them to be restored afterwards */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005325 if (is_hidden_fd(sv, fd))
5326 i |= COPYFD_RESTORE;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005327 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005328 if (fd == 2)
5329 copied_fd2 = i;
5330 sv->two_fd[sv_pos].orig = fd;
5331 sv->two_fd[sv_pos].copy = i;
5332 sv_pos++;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005333 }
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005334 if (newfd < 0) {
5335 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
Denis Vlasenko22f74142008-07-24 22:34:43 +00005336 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005337 /* Don't want to trigger debugging */
5338 if (fd != -1)
5339 close(fd);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005340 } else {
5341 copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005342 }
Denis Vlasenko5a867312008-07-24 19:46:38 +00005343 } else if (fd != newfd) { /* move newfd to fd */
5344 copyfd(newfd, fd | COPYFD_EXACT);
Denis Vlasenko559691a2008-10-05 18:39:31 +00005345#if ENABLE_ASH_BASH_COMPAT
5346 if (!(redir->nfile.type == NTO2 && fd == 2))
5347#endif
5348 close(newfd);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005349 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005350#if ENABLE_ASH_BASH_COMPAT
5351 if (redir->nfile.type == NTO2 && fd == 1) {
5352 /* We already redirected it to fd 1, now copy it to 2 */
5353 newfd = 1;
5354 fd = 2;
5355 goto redirect_more;
5356 }
5357#endif
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005358 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005359
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005360 INT_ON;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005361 if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5362 preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005363}
5364
5365/*
5366 * Undo the effects of the last redirection.
5367 */
5368static void
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005369popredir(int drop, int restore)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005370{
5371 struct redirtab *rp;
5372 int i;
5373
Denis Vlasenko01631112007-12-16 17:20:38 +00005374 if (--g_nullredirs >= 0)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005375 return;
5376 INT_OFF;
5377 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005378 for (i = 0; i < rp->pair_count; i++) {
5379 int fd = rp->two_fd[i].orig;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005380 int copy = rp->two_fd[i].copy;
5381 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005382 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005383 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005384 continue;
5385 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005386 if (copy != EMPTY) {
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005387 if (!drop || (restore && (copy & COPYFD_RESTORE))) {
Denis Vlasenko22f74142008-07-24 22:34:43 +00005388 copy &= ~COPYFD_RESTORE;
Denis Vlasenko5a867312008-07-24 19:46:38 +00005389 /*close(fd);*/
Denis Vlasenko22f74142008-07-24 22:34:43 +00005390 copyfd(copy, fd | COPYFD_EXACT);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005391 }
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00005392 close(copy & ~COPYFD_RESTORE);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005393 }
5394 }
5395 redirlist = rp->next;
Denis Vlasenko01631112007-12-16 17:20:38 +00005396 g_nullredirs = rp->nullredirs;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005397 free(rp);
5398 INT_ON;
5399}
5400
5401/*
5402 * Undo all redirections. Called on error or interrupt.
5403 */
5404
5405/*
5406 * Discard all saved file descriptors.
5407 */
5408static void
5409clearredir(int drop)
5410{
5411 for (;;) {
Denis Vlasenko01631112007-12-16 17:20:38 +00005412 g_nullredirs = 0;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005413 if (!redirlist)
5414 break;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005415 popredir(drop, /*restore:*/ 0);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005416 }
5417}
5418
5419static int
5420redirectsafe(union node *redir, int flags)
5421{
5422 int err;
5423 volatile int saveint;
5424 struct jmploc *volatile savehandler = exception_handler;
5425 struct jmploc jmploc;
5426
5427 SAVE_INT(saveint);
Denis Vlasenko5a867312008-07-24 19:46:38 +00005428 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5429 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005430 if (!err) {
5431 exception_handler = &jmploc;
5432 redirect(redir, flags);
5433 }
5434 exception_handler = savehandler;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00005435 if (err && exception_type != EXERROR)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005436 longjmp(exception_handler->loc, 1);
5437 RESTORE_INT(saveint);
5438 return err;
5439}
5440
5441
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005442/* ============ Routines to expand arguments to commands
5443 *
5444 * We have to deal with backquotes, shell variables, and file metacharacters.
5445 */
5446
Mike Frysinger98c52642009-04-02 10:02:37 +00005447#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005448static arith_t
5449ash_arith(const char *s)
5450{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005451 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005452 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005453
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005454 math_state.lookupvar = lookupvar;
5455 math_state.setvar = setvar2;
5456 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005457
5458 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005459 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005460 if (math_state.errmsg)
5461 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005462 INT_ON;
5463
5464 return result;
5465}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005466#endif
5467
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005468/*
5469 * expandarg flags
5470 */
5471#define EXP_FULL 0x1 /* perform word splitting & file globbing */
5472#define EXP_TILDE 0x2 /* do normal tilde expansion */
5473#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
5474#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
5475#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
5476#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
5477#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
5478#define EXP_WORD 0x80 /* expand word in parameter expansion */
5479#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
5480/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005481 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005482 */
5483#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
5484#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
5485#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
5486#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
5487#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
5488
5489/*
5490 * Structure specifying which parts of the string should be searched
5491 * for IFS characters.
5492 */
5493struct ifsregion {
5494 struct ifsregion *next; /* next region in list */
5495 int begoff; /* offset of start of region */
5496 int endoff; /* offset of end of region */
5497 int nulonly; /* search for nul bytes only */
5498};
5499
5500struct arglist {
5501 struct strlist *list;
5502 struct strlist **lastp;
5503};
5504
5505/* output of current string */
5506static char *expdest;
5507/* list of back quote expressions */
5508static struct nodelist *argbackq;
5509/* first struct in list of ifs regions */
5510static struct ifsregion ifsfirst;
5511/* last struct in list */
5512static struct ifsregion *ifslastp;
5513/* holds expanded arg list */
5514static struct arglist exparg;
5515
5516/*
5517 * Our own itoa().
5518 */
Denys Vlasenko26777aa2010-11-22 23:49:10 +01005519#if !ENABLE_SH_MATH_SUPPORT
5520/* cvtnum() is used even if math support is off (to prepare $? values and such) */
5521typedef long arith_t;
5522# define ARITH_FMT "%ld"
5523#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005524static int
5525cvtnum(arith_t num)
5526{
5527 int len;
5528
5529 expdest = makestrspace(32, expdest);
Denys Vlasenkobed7c812010-09-16 11:50:46 +02005530 len = fmtstr(expdest, 32, ARITH_FMT, num);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005531 STADJUST(len, expdest);
5532 return len;
5533}
5534
5535static size_t
5536esclen(const char *start, const char *p)
5537{
5538 size_t esc = 0;
5539
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005540 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005541 esc++;
5542 }
5543 return esc;
5544}
5545
5546/*
5547 * Remove any CTLESC characters from a string.
5548 */
5549static char *
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005550rmescapes(char *str, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005551{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005552 static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00005553
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005554 char *p, *q, *r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005555 unsigned inquotes;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005556 unsigned protect_against_glob;
5557 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005558
5559 p = strpbrk(str, qchars);
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005560 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005561 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005562
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005563 q = p;
5564 r = str;
5565 if (flag & RMESCAPE_ALLOC) {
5566 size_t len = p - str;
5567 size_t fulllen = len + strlen(p) + 1;
5568
5569 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02005570 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005571 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02005572 /* p and str may be invalidated by makestrspace */
5573 str = (char *)stackblock() + strloc;
5574 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005575 } else if (flag & RMESCAPE_HEAP) {
5576 r = ckmalloc(fulllen);
5577 } else {
5578 r = stalloc(fulllen);
5579 }
5580 q = r;
5581 if (len > 0) {
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005582 q = (char *)memcpy(q, str, len) + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005583 }
5584 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005585
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005586 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5587 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005588 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005589 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005590 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005591// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
5592// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
5593// Note: both inquotes and protect_against_glob only affect whether
5594// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005595 inquotes = ~inquotes;
5596 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005597 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005598 continue;
5599 }
5600 if (*p == '\\') {
5601 /* naked back slash */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005602 protect_against_glob = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005603 goto copy;
5604 }
Denys Vlasenkocd716832009-11-28 22:14:02 +01005605 if ((unsigned char)*p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005606 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005607 if (protect_against_glob && inquotes && *p != '/') {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005608 *q++ = '\\';
5609 }
5610 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005611 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005612 copy:
5613 *q++ = *p++;
5614 }
5615 *q = '\0';
5616 if (flag & RMESCAPE_GROW) {
5617 expdest = r;
5618 STADJUST(q - r + 1, expdest);
5619 }
5620 return r;
5621}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005622#define pmatch(a, b) !fnmatch((a), (b), 0)
5623
5624/*
5625 * Prepare a pattern for a expmeta (internal glob(3)) call.
5626 *
5627 * Returns an stalloced string.
5628 */
5629static char *
5630preglob(const char *pattern, int quoted, int flag)
5631{
5632 flag |= RMESCAPE_GLOB;
5633 if (quoted) {
5634 flag |= RMESCAPE_QUOTED;
5635 }
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005636 return rmescapes((char *)pattern, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005637}
5638
5639/*
5640 * Put a string on the stack.
5641 */
5642static void
5643memtodest(const char *p, size_t len, int syntax, int quotes)
5644{
5645 char *q = expdest;
5646
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005647 q = makestrspace(quotes ? len * 2 : len, q);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005648
5649 while (len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01005650 unsigned char c = *p++;
5651 if (c == '\0')
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005652 continue;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005653 if (quotes) {
5654 int n = SIT(c, syntax);
5655 if (n == CCTL || n == CBACK)
5656 USTPUTC(CTLESC, q);
5657 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005658 USTPUTC(c, q);
5659 }
5660
5661 expdest = q;
5662}
5663
5664static void
5665strtodest(const char *p, int syntax, int quotes)
5666{
5667 memtodest(p, strlen(p), syntax, quotes);
5668}
5669
5670/*
5671 * Record the fact that we have to scan this region of the
5672 * string for IFS characters.
5673 */
5674static void
5675recordregion(int start, int end, int nulonly)
5676{
5677 struct ifsregion *ifsp;
5678
5679 if (ifslastp == NULL) {
5680 ifsp = &ifsfirst;
5681 } else {
5682 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00005683 ifsp = ckzalloc(sizeof(*ifsp));
5684 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005685 ifslastp->next = ifsp;
5686 INT_ON;
5687 }
5688 ifslastp = ifsp;
5689 ifslastp->begoff = start;
5690 ifslastp->endoff = end;
5691 ifslastp->nulonly = nulonly;
5692}
5693
5694static void
5695removerecordregions(int endoff)
5696{
5697 if (ifslastp == NULL)
5698 return;
5699
5700 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005701 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005702 struct ifsregion *ifsp;
5703 INT_OFF;
5704 ifsp = ifsfirst.next->next;
5705 free(ifsfirst.next);
5706 ifsfirst.next = ifsp;
5707 INT_ON;
5708 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005709 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005710 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005711 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005712 ifslastp = &ifsfirst;
5713 ifsfirst.endoff = endoff;
5714 }
5715 return;
5716 }
5717
5718 ifslastp = &ifsfirst;
5719 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005720 ifslastp = ifslastp->next;
5721 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005722 struct ifsregion *ifsp;
5723 INT_OFF;
5724 ifsp = ifslastp->next->next;
5725 free(ifslastp->next);
5726 ifslastp->next = ifsp;
5727 INT_ON;
5728 }
5729 if (ifslastp->endoff > endoff)
5730 ifslastp->endoff = endoff;
5731}
5732
5733static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005734exptilde(char *startp, char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005735{
Denys Vlasenkocd716832009-11-28 22:14:02 +01005736 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005737 char *name;
5738 struct passwd *pw;
5739 const char *home;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02005740 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005741 int startloc;
5742
5743 name = p + 1;
5744
5745 while ((c = *++p) != '\0') {
5746 switch (c) {
5747 case CTLESC:
5748 return startp;
5749 case CTLQUOTEMARK:
5750 return startp;
5751 case ':':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005752 if (flags & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005753 goto done;
5754 break;
5755 case '/':
5756 case CTLENDVAR:
5757 goto done;
5758 }
5759 }
5760 done:
5761 *p = '\0';
5762 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02005763 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005764 } else {
5765 pw = getpwnam(name);
5766 if (pw == NULL)
5767 goto lose;
5768 home = pw->pw_dir;
5769 }
5770 if (!home || !*home)
5771 goto lose;
5772 *p = c;
5773 startloc = expdest - (char *)stackblock();
5774 strtodest(home, SQSYNTAX, quotes);
5775 recordregion(startloc, expdest - (char *)stackblock(), 0);
5776 return p;
5777 lose:
5778 *p = c;
5779 return startp;
5780}
5781
5782/*
5783 * Execute a command inside back quotes. If it's a builtin command, we
5784 * want to save its output in a block obtained from malloc. Otherwise
5785 * we fork off a subprocess and get the output of the command via a pipe.
5786 * Should be called with interrupts off.
5787 */
5788struct backcmd { /* result of evalbackcmd */
5789 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005790 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005791 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005792 struct job *jp; /* job structure for command */
5793};
5794
5795/* These forward decls are needed to use "eval" code for backticks handling: */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005796static uint8_t back_exitstatus; /* exit status of backquoted command */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005797#define EV_EXIT 01 /* exit after evaluating tree */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02005798static void evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005799
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02005800static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005801evalbackcmd(union node *n, struct backcmd *result)
5802{
5803 int saveherefd;
5804
5805 result->fd = -1;
5806 result->buf = NULL;
5807 result->nleft = 0;
5808 result->jp = NULL;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005809 if (n == NULL)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005810 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005811
5812 saveherefd = herefd;
5813 herefd = -1;
5814
5815 {
5816 int pip[2];
5817 struct job *jp;
5818
5819 if (pipe(pip) < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00005820 ash_msg_and_raise_error("pipe call failed");
Denis Vlasenko68404f12008-03-17 09:00:54 +00005821 jp = makejob(/*n,*/ 1);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005822 if (forkshell(jp, n, FORK_NOJOB) == 0) {
5823 FORCE_INT_ON;
5824 close(pip[0]);
5825 if (pip[1] != 1) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005826 /*close(1);*/
5827 copyfd(pip[1], 1 | COPYFD_EXACT);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005828 close(pip[1]);
5829 }
5830 eflag = 0;
5831 evaltree(n, EV_EXIT); /* actually evaltreenr... */
5832 /* NOTREACHED */
5833 }
5834 close(pip[1]);
5835 result->fd = pip[0];
5836 result->jp = jp;
5837 }
5838 herefd = saveherefd;
5839 out:
5840 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
5841 result->fd, result->buf, result->nleft, result->jp));
5842}
5843
5844/*
5845 * Expand stuff in backwards quotes.
5846 */
5847static void
5848expbackq(union node *cmd, int quoted, int quotes)
5849{
5850 struct backcmd in;
5851 int i;
5852 char buf[128];
5853 char *p;
5854 char *dest;
5855 int startloc;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00005856 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005857 struct stackmark smark;
5858
5859 INT_OFF;
5860 setstackmark(&smark);
5861 dest = expdest;
5862 startloc = dest - (char *)stackblock();
5863 grabstackstr(dest);
5864 evalbackcmd(cmd, &in);
5865 popstackmark(&smark);
5866
5867 p = in.buf;
5868 i = in.nleft;
5869 if (i == 0)
5870 goto read;
5871 for (;;) {
5872 memtodest(p, i, syntax, quotes);
5873 read:
5874 if (in.fd < 0)
5875 break;
Denis Vlasenkoe376d452008-02-20 22:23:24 +00005876 i = nonblock_safe_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005877 TRACE(("expbackq: read returns %d\n", i));
5878 if (i <= 0)
5879 break;
5880 p = buf;
5881 }
5882
Denis Vlasenko60818682007-09-28 22:07:23 +00005883 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005884 if (in.fd >= 0) {
5885 close(in.fd);
5886 back_exitstatus = waitforjob(in.jp);
5887 }
5888 INT_ON;
5889
5890 /* Eat all trailing newlines */
5891 dest = expdest;
5892 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
5893 STUNPUTC(dest);
5894 expdest = dest;
5895
5896 if (quoted == 0)
5897 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02005898 TRACE(("evalbackq: size:%d:'%.*s'\n",
5899 (int)((dest - (char *)stackblock()) - startloc),
5900 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005901 stackblock() + startloc));
5902}
5903
Mike Frysinger98c52642009-04-02 10:02:37 +00005904#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005905/*
5906 * Expand arithmetic expression. Backup to start of expression,
5907 * evaluate, place result in (backed up) result, adjust string position.
5908 */
5909static void
5910expari(int quotes)
5911{
5912 char *p, *start;
5913 int begoff;
5914 int flag;
5915 int len;
5916
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00005917 /* ifsfree(); */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005918
5919 /*
5920 * This routine is slightly over-complicated for
5921 * efficiency. Next we scan backwards looking for the
5922 * start of arithmetic.
5923 */
5924 start = stackblock();
5925 p = expdest - 1;
5926 *p = '\0';
5927 p--;
5928 do {
5929 int esc;
5930
Denys Vlasenkocd716832009-11-28 22:14:02 +01005931 while ((unsigned char)*p != CTLARI) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005932 p--;
5933#if DEBUG
5934 if (p < start) {
5935 ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
5936 }
5937#endif
5938 }
5939
5940 esc = esclen(start, p);
5941 if (!(esc % 2)) {
5942 break;
5943 }
5944
5945 p -= esc + 1;
5946 } while (1);
5947
5948 begoff = p - start;
5949
5950 removerecordregions(begoff);
5951
5952 flag = p[1];
5953
5954 expdest = p;
5955
5956 if (quotes)
Denys Vlasenkob6c84342009-08-29 20:23:20 +02005957 rmescapes(p + 2, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005958
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005959 len = cvtnum(ash_arith(p + 2));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005960
5961 if (flag != '"')
5962 recordregion(begoff, begoff + len, 0);
5963}
5964#endif
5965
5966/* argstr needs it */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005967static char *evalvar(char *p, int flags, struct strlist *var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005968
5969/*
5970 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
5971 * characters to allow for further processing. Otherwise treat
5972 * $@ like $* since no splitting will be performed.
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00005973 *
5974 * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
5975 * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
5976 * for correct expansion of "B=$A" word.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005977 */
5978static void
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005979argstr(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005980{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00005981 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005982 '=',
5983 ':',
5984 CTLQUOTEMARK,
5985 CTLENDVAR,
5986 CTLESC,
5987 CTLVAR,
5988 CTLBACKQ,
5989 CTLBACKQ | CTLQUOTE,
Mike Frysinger98c52642009-04-02 10:02:37 +00005990#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005991 CTLENDARI,
5992#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01005993 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005994 };
5995 const char *reject = spclchars;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02005996 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
5997 int breakall = flags & EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005998 int inquotes;
5999 size_t length;
6000 int startloc;
6001
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006002 if (!(flags & EXP_VARTILDE)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006003 reject += 2;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006004 } else if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006005 reject++;
6006 }
6007 inquotes = 0;
6008 length = 0;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006009 if (flags & EXP_TILDE) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006010 char *q;
6011
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006012 flags &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006013 tilde:
6014 q = p;
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006015 if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006016 q++;
6017 if (*q == '~')
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006018 p = exptilde(p, q, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006019 }
6020 start:
6021 startloc = expdest - (char *)stackblock();
6022 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006023 unsigned char c;
6024
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006025 length += strcspn(p + length, reject);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006026 c = p[length];
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006027 if (c) {
6028 if (!(c & 0x80)
Denys Vlasenko958581a2010-09-12 15:04:27 +02006029 IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006030 ) {
6031 /* c == '=' || c == ':' || c == CTLENDARI */
6032 length++;
6033 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006034 }
6035 if (length > 0) {
6036 int newloc;
6037 expdest = stack_nputstr(p, length, expdest);
6038 newloc = expdest - (char *)stackblock();
6039 if (breakall && !inquotes && newloc > startloc) {
6040 recordregion(startloc, newloc, 0);
6041 }
6042 startloc = newloc;
6043 }
6044 p += length + 1;
6045 length = 0;
6046
6047 switch (c) {
6048 case '\0':
6049 goto breakloop;
6050 case '=':
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006051 if (flags & EXP_VARTILDE2) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006052 p--;
6053 continue;
6054 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006055 flags |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006056 reject++;
6057 /* fall through */
6058 case ':':
6059 /*
6060 * sort of a hack - expand tildes in variable
6061 * assignments (after the first '=' and after ':'s).
6062 */
6063 if (*--p == '~') {
6064 goto tilde;
6065 }
6066 continue;
6067 }
6068
6069 switch (c) {
6070 case CTLENDVAR: /* ??? */
6071 goto breakloop;
6072 case CTLQUOTEMARK:
6073 /* "$@" syntax adherence hack */
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006074 if (!inquotes
6075 && memcmp(p, dolatstr, 4) == 0
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006076 && ( p[4] == (char)CTLQUOTEMARK
6077 || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006078 )
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006079 ) {
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006080 p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006081 goto start;
6082 }
6083 inquotes = !inquotes;
6084 addquote:
6085 if (quotes) {
6086 p--;
6087 length++;
6088 startloc++;
6089 }
6090 break;
6091 case CTLESC:
6092 startloc++;
6093 length++;
6094 goto addquote;
6095 case CTLVAR:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006096 p = evalvar(p, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006097 goto start;
6098 case CTLBACKQ:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006099 c = '\0';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006100 case CTLBACKQ|CTLQUOTE:
6101 expbackq(argbackq->n, c, quotes);
6102 argbackq = argbackq->next;
6103 goto start;
Mike Frysinger98c52642009-04-02 10:02:37 +00006104#if ENABLE_SH_MATH_SUPPORT
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006105 case CTLENDARI:
6106 p--;
6107 expari(quotes);
6108 goto start;
6109#endif
6110 }
6111 }
Denys Vlasenko958581a2010-09-12 15:04:27 +02006112 breakloop: ;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006113}
6114
6115static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006116scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6117 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006118{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006119 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006120 char c;
6121
6122 loc = startp;
6123 loc2 = rmesc;
6124 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006125 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006126 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006127
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006128 c = *loc2;
6129 if (zero) {
6130 *loc2 = '\0';
6131 s = rmesc;
6132 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006133 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006134
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006135 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006136 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006137 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006138 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006139 loc++;
6140 loc++;
6141 loc2++;
6142 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006143 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006144}
6145
6146static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006147scanright(char *startp, char *rmesc, char *rmescend,
6148 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006149{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006150#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6151 int try2optimize = match_at_start;
6152#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006153 int esc = 0;
6154 char *loc;
6155 char *loc2;
6156
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006157 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6158 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6159 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6160 * Logic:
6161 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6162 * and on each iteration they go back two/one char until they reach the beginning.
6163 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6164 */
6165 /* TODO: document in what other circumstances we are called. */
6166
6167 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006168 int match;
6169 char c = *loc2;
6170 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006171 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006172 *loc2 = '\0';
6173 s = rmesc;
6174 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006175 match = pmatch(pattern, s);
6176 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006177 *loc2 = c;
6178 if (match)
6179 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006180#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6181 if (try2optimize) {
6182 /* Maybe we can optimize this:
6183 * if pattern ends with unescaped *, we can avoid checking
6184 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
6185 * it wont match truncated "raw_value_of_" strings too.
6186 */
6187 unsigned plen = strlen(pattern);
6188 /* Does it end with "*"? */
6189 if (plen != 0 && pattern[--plen] == '*') {
6190 /* "xxxx*" is not escaped */
6191 /* "xxx\*" is escaped */
6192 /* "xx\\*" is not escaped */
6193 /* "x\\\*" is escaped */
6194 int slashes = 0;
6195 while (plen != 0 && pattern[--plen] == '\\')
6196 slashes++;
6197 if (!(slashes & 1))
6198 break; /* ends with unescaped "*" */
6199 }
6200 try2optimize = 0;
6201 }
6202#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006203 loc--;
6204 if (quotes) {
6205 if (--esc < 0) {
6206 esc = esclen(startp, loc);
6207 }
6208 if (esc % 2) {
6209 esc--;
6210 loc--;
6211 }
6212 }
6213 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006214 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006215}
6216
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006217static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006218static void
6219varunset(const char *end, const char *var, const char *umsg, int varflags)
6220{
6221 const char *msg;
6222 const char *tail;
6223
6224 tail = nullstr;
6225 msg = "parameter not set";
6226 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006227 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006228 if (varflags & VSNUL)
6229 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006230 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006231 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006232 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006233 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006234 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006235}
6236
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006237#if ENABLE_ASH_BASH_COMPAT
6238static char *
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006239parse_sub_pattern(char *arg, int varflags)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006240{
6241 char *idx, *repl = NULL;
6242 unsigned char c;
6243
Denys Vlasenko16149002010-08-06 22:06:21 +02006244 //char *org_arg = arg;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006245 //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
Denis Vlasenko2659c632008-06-14 06:04:59 +00006246 idx = arg;
6247 while (1) {
6248 c = *arg;
6249 if (!c)
6250 break;
6251 if (c == '/') {
6252 /* Only the first '/' seen is our separator */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006253 if (!repl) {
Denis Vlasenko2659c632008-06-14 06:04:59 +00006254 repl = idx + 1;
6255 c = '\0';
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006256 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006257 }
Denis Vlasenko2659c632008-06-14 06:04:59 +00006258 *idx++ = c;
Denis Vlasenko2659c632008-06-14 06:04:59 +00006259 arg++;
Denys Vlasenko33bbb272010-08-07 22:24:36 +02006260 /*
6261 * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
6262 * The result is a_\_z_c (not a\_\_z_c)!
6263 *
6264 * Enable debug prints in this function and you'll see:
6265 * ash: arg:'\\b/_\\_z_' varflags:d
6266 * ash: pattern:'\\b' repl:'_\_z_'
6267 * That is, \\b is interpreted as \\b, but \\_ as \_!
6268 * IOW: search pattern and replace string treat backslashes
6269 * differently! That is the reason why we check repl below:
6270 */
6271 if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
6272 arg++; /* skip both '\', not just first one */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006273 }
Denis Vlasenko29038c02008-06-14 06:14:02 +00006274 *idx = c; /* NUL */
Denys Vlasenko16149002010-08-06 22:06:21 +02006275 //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006276
6277 return repl;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006278}
6279#endif /* ENABLE_ASH_BASH_COMPAT */
6280
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281static const char *
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006282subevalvar(char *p, char *varname, int strloc, int subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006283 int startloc, int varflags, int quotes, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006284{
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006285 struct nodelist *saveargbackq = argbackq;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006286 char *startp;
6287 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006288 char *rmesc, *rmescend;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006289 char *str;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006290 IF_ASH_BASH_COMPAT(const char *repl = NULL;)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006291 IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006292 int saveherefd = herefd;
6293 int amount, workloc, resetloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006295 char *(*scan)(char*, char*, char*, char*, int, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006297 //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
6298 // p, varname, strloc, subtype, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006299
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006300 herefd = -1;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006301 argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
6302 var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006303 STPUTC('\0', expdest);
6304 herefd = saveherefd;
6305 argbackq = saveargbackq;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006306 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006307
6308 switch (subtype) {
6309 case VSASSIGN:
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006310 setvar(varname, startp, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006311 amount = startp - expdest;
6312 STADJUST(amount, expdest);
6313 return startp;
6314
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006315 case VSQUESTION:
6316 varunset(p, varname, startp, varflags);
6317 /* NOTREACHED */
6318
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006319#if ENABLE_ASH_BASH_COMPAT
6320 case VSSUBSTR:
6321 loc = str = stackblock() + strloc;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006322 /* Read POS in ${var:POS:LEN} */
6323 pos = atoi(loc); /* number(loc) errors out on "1:4" */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006324 len = str - startp - 1;
6325
6326 /* *loc != '\0', guaranteed by parser */
6327 if (quotes) {
6328 char *ptr;
6329
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006330 /* Adjust the length by the number of escapes */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006331 for (ptr = startp; ptr < (str - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006332 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006333 len--;
6334 ptr++;
6335 }
6336 }
6337 }
6338 orig_len = len;
6339
6340 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006341 /* ${var::LEN} */
6342 len = number(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006343 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006344 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006345 len = orig_len;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006346 while (*loc && *loc != ':') {
6347 /* TODO?
6348 * bash complains on: var=qwe; echo ${var:1a:123}
6349 if (!isdigit(*loc))
6350 ash_msg_and_raise_error(msg_illnum, str);
6351 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006352 loc++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006353 }
6354 if (*loc++ == ':') {
6355 len = number(loc);
6356 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006357 }
6358 if (pos >= orig_len) {
6359 pos = 0;
6360 len = 0;
6361 }
6362 if (len > (orig_len - pos))
6363 len = orig_len - pos;
6364
6365 for (str = startp; pos; str++, pos--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006366 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006367 str++;
6368 }
6369 for (loc = startp; len; len--) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006370 if (quotes && (unsigned char)*str == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006371 *loc++ = *str++;
6372 *loc++ = *str++;
6373 }
6374 *loc = '\0';
6375 amount = loc - expdest;
6376 STADJUST(amount, expdest);
6377 return loc;
6378#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006379 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006380
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006381 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006382
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006383 /* We'll comeback here if we grow the stack while handling
6384 * a VSREPLACE or VSREPLACEALL, since our pointers into the
6385 * stack will need rebasing, and we'll need to remove our work
6386 * areas each time
6387 */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00006388 IF_ASH_BASH_COMPAT(restart:)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006389
6390 amount = expdest - ((char *)stackblock() + resetloc);
6391 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006392 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006393
6394 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006395 rmescend = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006396 if (quotes) {
Denys Vlasenkob6c84342009-08-29 20:23:20 +02006397 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006398 if (rmesc != startp) {
6399 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006400 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006401 }
6402 }
6403 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00006404 str = (char *)stackblock() + strloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006405 preglob(str, varflags & VSQUOTE, 0);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006406 workloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006407
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006408#if ENABLE_ASH_BASH_COMPAT
6409 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006410 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006411
Denis Vlasenkod6855d12008-09-27 14:03:25 +00006412 if (!repl) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006413 repl = parse_sub_pattern(str, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006414 //bb_error_msg("repl:'%s'", repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006415 if (!repl)
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006416 repl = nullstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006417 }
6418
6419 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006420 if (str[0] == '\0')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006421 return NULL;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006422
6423 len = 0;
6424 idx = startp;
6425 end = str - 1;
6426 while (idx < end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006427 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006428 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006429 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006430 if (!loc) {
6431 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006432 char *restart_detect = stackblock();
6433 skip_matching:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006434 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01006435 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006436 idx++;
6437 len++;
6438 STPUTC(*idx, expdest);
6439 }
6440 if (stackblock() != restart_detect)
6441 goto restart;
6442 idx++;
6443 len++;
6444 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006445 /* continue; - prone to quadratic behavior, smarter code: */
6446 if (idx >= end)
6447 break;
6448 if (str[0] == '*') {
6449 /* Pattern is "*foo". If "*foo" does not match "long_string",
6450 * it would never match "ong_string" etc, no point in trying.
6451 */
6452 goto skip_matching;
6453 }
6454 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006455 }
6456
6457 if (subtype == VSREPLACEALL) {
6458 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006459 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006460 idx++;
6461 idx++;
6462 rmesc++;
6463 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006464 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006465 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006466 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006467
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006468 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006469 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006470 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006471 if (quotes && *loc == '\\') {
6472 STPUTC(CTLESC, expdest);
6473 len++;
6474 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006475 STPUTC(*loc, expdest);
6476 if (stackblock() != restart_detect)
6477 goto restart;
6478 len++;
6479 }
6480
6481 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02006482 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006483 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006484 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006485 STPUTC(*idx, expdest);
6486 if (stackblock() != restart_detect)
6487 goto restart;
6488 len++;
6489 idx++;
6490 }
6491 break;
6492 }
6493 }
6494
6495 /* We've put the replaced text into a buffer at workloc, now
6496 * move it to the right place and adjust the stack.
6497 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006498 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006499 startp = (char *)stackblock() + startloc;
6500 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006501 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006502 amount = expdest - (startp + len);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006503 STADJUST(-amount, expdest);
6504 return startp;
6505 }
6506#endif /* ENABLE_ASH_BASH_COMPAT */
6507
6508 subtype -= VSTRIMRIGHT;
6509#if DEBUG
6510 if (subtype < 0 || subtype > 7)
6511 abort();
6512#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006513 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006514 zero = subtype >> 1;
6515 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
6516 scan = (subtype & 1) ^ zero ? scanleft : scanright;
6517
6518 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
6519 if (loc) {
6520 if (zero) {
6521 memmove(startp, loc, str - loc);
6522 loc = startp + (str - loc) - 1;
6523 }
6524 *loc = '\0';
6525 amount = loc - expdest;
6526 STADJUST(amount, expdest);
6527 }
6528 return loc;
6529}
6530
6531/*
6532 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006533 * name parameter (examples):
6534 * ash -c 'echo $1' name:'1='
6535 * ash -c 'echo $qwe' name:'qwe='
6536 * ash -c 'echo $$' name:'$='
6537 * ash -c 'echo ${$}' name:'$='
6538 * ash -c 'echo ${$##q}' name:'$=q'
6539 * ash -c 'echo ${#$}' name:'$='
6540 * note: examples with bad shell syntax:
6541 * ash -c 'echo ${#$1}' name:'$=1'
6542 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006543 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02006544static NOINLINE ssize_t
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006545varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006546{
Mike Frysinger98c52642009-04-02 10:02:37 +00006547 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006548 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006549 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006550 int sepq = 0;
6551 ssize_t len = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 int subtype = varflags & VSTYPE;
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006553 int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006554 int quoted = varflags & VSQUOTE;
6555 int syntax = quoted ? DQSYNTAX : BASESYNTAX;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006556
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006557 switch (*name) {
6558 case '$':
6559 num = rootpid;
6560 goto numvar;
6561 case '?':
6562 num = exitstatus;
6563 goto numvar;
6564 case '#':
6565 num = shellparam.nparam;
6566 goto numvar;
6567 case '!':
6568 num = backgndpid;
6569 if (num == 0)
6570 return -1;
6571 numvar:
6572 len = cvtnum(num);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006573 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006574 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00006575 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006576 for (i = NOPTS - 1; i >= 0; i--) {
6577 if (optlist[i]) {
Mike Frysinger98c52642009-04-02 10:02:37 +00006578 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006579 len++;
6580 }
6581 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02006582 check_1char_name:
6583#if 0
6584 /* handles cases similar to ${#$1} */
6585 if (name[2] != '\0')
6586 raise_error_syntax("bad substitution");
6587#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006588 break;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006589 case '@': {
6590 char **ap;
6591 int sep;
6592
6593 if (quoted && (flags & EXP_FULL)) {
6594 /* note: this is not meant as PEOF value */
6595 sep = 1 << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006596 goto param;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006597 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006598 /* fall through */
6599 case '*':
Denys Vlasenkocd716832009-11-28 22:14:02 +01006600 sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006601 i = SIT(sep, syntax);
6602 if (quotes && (i == CCTL || i == CBACK))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006603 sepq = 1;
6604 param:
6605 ap = shellparam.p;
6606 if (!ap)
6607 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02006608 while ((p = *ap++) != NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006609 size_t partlen;
6610
6611 partlen = strlen(p);
6612 len += partlen;
6613
6614 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6615 memtodest(p, partlen, syntax, quotes);
6616
6617 if (*ap && sep) {
6618 char *q;
6619
6620 len++;
6621 if (subtype == VSPLUS || subtype == VSLENGTH) {
6622 continue;
6623 }
6624 q = expdest;
6625 if (sepq)
6626 STPUTC(CTLESC, q);
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006627 /* note: may put NUL despite sep != 0
6628 * (see sep = 1 << CHAR_BIT above) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006629 STPUTC(sep, q);
6630 expdest = q;
6631 }
6632 }
6633 return len;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006634 } /* case '@' and '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006635 case '0':
6636 case '1':
6637 case '2':
6638 case '3':
6639 case '4':
6640 case '5':
6641 case '6':
6642 case '7':
6643 case '8':
6644 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02006645 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006646 if (num < 0 || num > shellparam.nparam)
6647 return -1;
6648 p = num ? shellparam.p[num - 1] : arg0;
6649 goto value;
6650 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006651 /* NB: name has form "VAR=..." */
6652
6653 /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
6654 * which should be considered before we check variables. */
6655 if (var_str_list) {
6656 unsigned name_len = (strchrnul(name, '=') - name) + 1;
6657 p = NULL;
6658 do {
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00006659 char *str, *eq;
6660 str = var_str_list->text;
6661 eq = strchr(str, '=');
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006662 if (!eq) /* stop at first non-assignment */
6663 break;
6664 eq++;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +00006665 if (name_len == (unsigned)(eq - str)
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01006666 && strncmp(str, name, name_len) == 0
6667 ) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006668 p = eq;
6669 /* goto value; - WRONG! */
6670 /* think "A=1 A=2 B=$A" */
6671 }
6672 var_str_list = var_str_list->next;
6673 } while (var_str_list);
6674 if (p)
6675 goto value;
6676 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006677 p = lookupvar(name);
6678 value:
6679 if (!p)
6680 return -1;
6681
6682 len = strlen(p);
6683 if (!(subtype == VSPLUS || subtype == VSLENGTH))
6684 memtodest(p, len, syntax, quotes);
6685 return len;
6686 }
6687
6688 if (subtype == VSPLUS || subtype == VSLENGTH)
6689 STADJUST(-len, expdest);
6690 return len;
6691}
6692
6693/*
6694 * Expand a variable, and return a pointer to the next character in the
6695 * input string.
6696 */
6697static char *
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006698evalvar(char *p, int flags, struct strlist *var_str_list)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006699{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006700 char varflags;
6701 char subtype;
6702 char quoted;
6703 char easy;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704 char *var;
6705 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006706 int startloc;
6707 ssize_t varlen;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006708
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006709 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006710 subtype = varflags & VSTYPE;
6711 quoted = varflags & VSQUOTE;
6712 var = p;
6713 easy = (!quoted || (*var == '@' && shellparam.nparam));
6714 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02006715 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716
6717 again:
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006718 varlen = varvalue(var, varflags, flags, var_str_list);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006719 if (varflags & VSNUL)
6720 varlen--;
6721
6722 if (subtype == VSPLUS) {
6723 varlen = -1 - varlen;
6724 goto vsplus;
6725 }
6726
6727 if (subtype == VSMINUS) {
6728 vsplus:
6729 if (varlen < 0) {
6730 argstr(
Denys Vlasenko6040fe82010-09-12 15:03:16 +02006731 p,
6732 flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006733 var_str_list
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006734 );
6735 goto end;
6736 }
6737 if (easy)
6738 goto record;
6739 goto end;
6740 }
6741
6742 if (subtype == VSASSIGN || subtype == VSQUESTION) {
6743 if (varlen < 0) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006744 if (subevalvar(p, var, /* strloc: */ 0,
6745 subtype, startloc, varflags,
6746 /* quotes: */ 0,
6747 var_str_list)
6748 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006749 varflags &= ~VSNUL;
6750 /*
6751 * Remove any recorded regions beyond
6752 * start of variable
6753 */
6754 removerecordregions(startloc);
6755 goto again;
6756 }
6757 goto end;
6758 }
6759 if (easy)
6760 goto record;
6761 goto end;
6762 }
6763
6764 if (varlen < 0 && uflag)
6765 varunset(p, var, 0, 0);
6766
6767 if (subtype == VSLENGTH) {
6768 cvtnum(varlen > 0 ? varlen : 0);
6769 goto record;
6770 }
6771
6772 if (subtype == VSNORMAL) {
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006773 if (easy)
6774 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006775 goto end;
6776 }
6777
6778#if DEBUG
6779 switch (subtype) {
6780 case VSTRIMLEFT:
6781 case VSTRIMLEFTMAX:
6782 case VSTRIMRIGHT:
6783 case VSTRIMRIGHTMAX:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006784#if ENABLE_ASH_BASH_COMPAT
6785 case VSSUBSTR:
6786 case VSREPLACE:
6787 case VSREPLACEALL:
6788#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 break;
6790 default:
6791 abort();
6792 }
6793#endif
6794
6795 if (varlen >= 0) {
6796 /*
6797 * Terminate the string and start recording the pattern
6798 * right after it
6799 */
6800 STPUTC('\0', expdest);
6801 patloc = expdest - (char *)stackblock();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006802 if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006803 startloc, varflags,
Denys Vlasenko1166d7b2009-09-16 16:20:31 +02006804//TODO: | EXP_REDIR too? All other such places do it too
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006805 /* quotes: */ flags & (EXP_FULL | EXP_CASE),
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006806 var_str_list)
6807 ) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006808 int amount = expdest - (
6809 (char *)stackblock() + patloc - 1
6810 );
6811 STADJUST(-amount, expdest);
6812 }
6813 /* Remove any recorded regions beyond start of variable */
6814 removerecordregions(startloc);
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00006815 record:
6816 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006817 }
6818
6819 end:
6820 if (subtype != VSNORMAL) { /* skip to end of alternative */
6821 int nesting = 1;
6822 for (;;) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006823 unsigned char c = *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006824 if (c == CTLESC)
6825 p++;
6826 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
6827 if (varlen >= 0)
6828 argbackq = argbackq->next;
6829 } else if (c == CTLVAR) {
6830 if ((*p++ & VSTYPE) != VSNORMAL)
6831 nesting++;
6832 } else if (c == CTLENDVAR) {
6833 if (--nesting == 0)
6834 break;
6835 }
6836 }
6837 }
6838 return p;
6839}
6840
6841/*
6842 * Break the argument string into pieces based upon IFS and add the
6843 * strings to the argument list. The regions of the string to be
6844 * searched for IFS characters have been stored by recordregion.
6845 */
6846static void
6847ifsbreakup(char *string, struct arglist *arglist)
6848{
6849 struct ifsregion *ifsp;
6850 struct strlist *sp;
6851 char *start;
6852 char *p;
6853 char *q;
6854 const char *ifs, *realifs;
6855 int ifsspc;
6856 int nulonly;
6857
6858 start = string;
6859 if (ifslastp != NULL) {
6860 ifsspc = 0;
6861 nulonly = 0;
6862 realifs = ifsset() ? ifsval() : defifs;
6863 ifsp = &ifsfirst;
6864 do {
6865 p = string + ifsp->begoff;
6866 nulonly = ifsp->nulonly;
6867 ifs = nulonly ? nullstr : realifs;
6868 ifsspc = 0;
6869 while (p < string + ifsp->endoff) {
6870 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006871 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006872 p++;
6873 if (!strchr(ifs, *p)) {
6874 p++;
6875 continue;
6876 }
6877 if (!nulonly)
6878 ifsspc = (strchr(defifs, *p) != NULL);
6879 /* Ignore IFS whitespace at start */
6880 if (q == start && ifsspc) {
6881 p++;
6882 start = p;
6883 continue;
6884 }
6885 *q = '\0';
Denis Vlasenko597906c2008-02-20 16:38:54 +00006886 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006887 sp->text = start;
6888 *arglist->lastp = sp;
6889 arglist->lastp = &sp->next;
6890 p++;
6891 if (!nulonly) {
6892 for (;;) {
6893 if (p >= string + ifsp->endoff) {
6894 break;
6895 }
6896 q = p;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006897 if ((unsigned char)*p == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006898 p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00006899 if (strchr(ifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006900 p = q;
6901 break;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006902 }
6903 if (strchr(defifs, *p) == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006904 if (ifsspc) {
6905 p++;
6906 ifsspc = 0;
6907 } else {
6908 p = q;
6909 break;
6910 }
6911 } else
6912 p++;
6913 }
6914 }
6915 start = p;
6916 } /* while */
6917 ifsp = ifsp->next;
6918 } while (ifsp != NULL);
6919 if (nulonly)
6920 goto add;
6921 }
6922
6923 if (!*start)
6924 return;
6925
6926 add:
Denis Vlasenko597906c2008-02-20 16:38:54 +00006927 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006928 sp->text = start;
6929 *arglist->lastp = sp;
6930 arglist->lastp = &sp->next;
6931}
6932
6933static void
6934ifsfree(void)
6935{
6936 struct ifsregion *p;
6937
6938 INT_OFF;
6939 p = ifsfirst.next;
6940 do {
6941 struct ifsregion *ifsp;
6942 ifsp = p->next;
6943 free(p);
6944 p = ifsp;
6945 } while (p);
6946 ifslastp = NULL;
6947 ifsfirst.next = NULL;
6948 INT_ON;
6949}
6950
6951/*
6952 * Add a file name to the list.
6953 */
6954static void
6955addfname(const char *name)
6956{
6957 struct strlist *sp;
6958
Denis Vlasenko597906c2008-02-20 16:38:54 +00006959 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006960 sp->text = ststrdup(name);
6961 *exparg.lastp = sp;
6962 exparg.lastp = &sp->next;
6963}
6964
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006965/*
6966 * Do metacharacter (i.e. *, ?, [...]) expansion.
6967 */
6968static void
Denys Vlasenkofd33e172010-06-26 22:55:44 +02006969expmeta(char *expdir, char *enddir, char *name)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006970{
6971 char *p;
6972 const char *cp;
6973 char *start;
6974 char *endname;
6975 int metaflag;
6976 struct stat statb;
6977 DIR *dirp;
6978 struct dirent *dp;
6979 int atend;
6980 int matchdot;
6981
6982 metaflag = 0;
6983 start = name;
6984 for (p = name; *p; p++) {
6985 if (*p == '*' || *p == '?')
6986 metaflag = 1;
6987 else if (*p == '[') {
6988 char *q = p + 1;
6989 if (*q == '!')
6990 q++;
6991 for (;;) {
6992 if (*q == '\\')
6993 q++;
6994 if (*q == '/' || *q == '\0')
6995 break;
6996 if (*++q == ']') {
6997 metaflag = 1;
6998 break;
6999 }
7000 }
7001 } else if (*p == '\\')
7002 p++;
7003 else if (*p == '/') {
7004 if (metaflag)
7005 goto out;
7006 start = p + 1;
7007 }
7008 }
7009 out:
7010 if (metaflag == 0) { /* we've reached the end of the file name */
7011 if (enddir != expdir)
7012 metaflag++;
7013 p = name;
7014 do {
7015 if (*p == '\\')
7016 p++;
7017 *enddir++ = *p;
7018 } while (*p++);
7019 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
7020 addfname(expdir);
7021 return;
7022 }
7023 endname = p;
7024 if (name < start) {
7025 p = name;
7026 do {
7027 if (*p == '\\')
7028 p++;
7029 *enddir++ = *p++;
7030 } while (p < start);
7031 }
7032 if (enddir == expdir) {
7033 cp = ".";
7034 } else if (enddir == expdir + 1 && *expdir == '/') {
7035 cp = "/";
7036 } else {
7037 cp = expdir;
7038 enddir[-1] = '\0';
7039 }
7040 dirp = opendir(cp);
7041 if (dirp == NULL)
7042 return;
7043 if (enddir != expdir)
7044 enddir[-1] = '/';
7045 if (*endname == 0) {
7046 atend = 1;
7047 } else {
7048 atend = 0;
7049 *endname++ = '\0';
7050 }
7051 matchdot = 0;
7052 p = start;
7053 if (*p == '\\')
7054 p++;
7055 if (*p == '.')
7056 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007057 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007058 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007059 continue;
7060 if (pmatch(start, dp->d_name)) {
7061 if (atend) {
7062 strcpy(enddir, dp->d_name);
7063 addfname(expdir);
7064 } else {
7065 for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
7066 continue;
7067 p[-1] = '/';
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007068 expmeta(expdir, p, endname);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007069 }
7070 }
7071 }
7072 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007073 if (!atend)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007074 endname[-1] = '/';
7075}
7076
7077static struct strlist *
7078msort(struct strlist *list, int len)
7079{
7080 struct strlist *p, *q = NULL;
7081 struct strlist **lpp;
7082 int half;
7083 int n;
7084
7085 if (len <= 1)
7086 return list;
7087 half = len >> 1;
7088 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007089 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007090 q = p;
7091 p = p->next;
7092 }
7093 q->next = NULL; /* terminate first half of list */
7094 q = msort(list, half); /* sort first half of list */
7095 p = msort(p, len - half); /* sort second half */
7096 lpp = &list;
7097 for (;;) {
7098#if ENABLE_LOCALE_SUPPORT
7099 if (strcoll(p->text, q->text) < 0)
7100#else
7101 if (strcmp(p->text, q->text) < 0)
7102#endif
7103 {
7104 *lpp = p;
7105 lpp = &p->next;
7106 p = *lpp;
7107 if (p == NULL) {
7108 *lpp = q;
7109 break;
7110 }
7111 } else {
7112 *lpp = q;
7113 lpp = &q->next;
7114 q = *lpp;
7115 if (q == NULL) {
7116 *lpp = p;
7117 break;
7118 }
7119 }
7120 }
7121 return list;
7122}
7123
7124/*
7125 * Sort the results of file name expansion. It calculates the number of
7126 * strings to sort and then calls msort (short for merge sort) to do the
7127 * work.
7128 */
7129static struct strlist *
7130expsort(struct strlist *str)
7131{
7132 int len;
7133 struct strlist *sp;
7134
7135 len = 0;
7136 for (sp = str; sp; sp = sp->next)
7137 len++;
7138 return msort(str, len);
7139}
7140
7141static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007142expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007143{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00007144 static const char metachars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007145 '*', '?', '[', 0
7146 };
7147 /* TODO - EXP_REDIR */
7148
7149 while (str) {
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007150 char *expdir;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007151 struct strlist **savelastp;
7152 struct strlist *sp;
7153 char *p;
7154
7155 if (fflag)
7156 goto nometa;
7157 if (!strpbrk(str->text, metachars))
7158 goto nometa;
7159 savelastp = exparg.lastp;
7160
7161 INT_OFF;
7162 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
7163 {
7164 int i = strlen(str->text);
7165 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
7166 }
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007167 expmeta(expdir, expdir, p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007168 free(expdir);
7169 if (p != str->text)
7170 free(p);
7171 INT_ON;
7172 if (exparg.lastp == savelastp) {
7173 /*
7174 * no matches
7175 */
7176 nometa:
7177 *exparg.lastp = str;
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007178 rmescapes(str->text, 0);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007179 exparg.lastp = &str->next;
7180 } else {
7181 *exparg.lastp = NULL;
7182 *savelastp = sp = expsort(*savelastp);
7183 while (sp->next != NULL)
7184 sp = sp->next;
7185 exparg.lastp = &sp->next;
7186 }
7187 str = str->next;
7188 }
7189}
7190
7191/*
7192 * Perform variable substitution and command substitution on an argument,
7193 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7194 * perform splitting and file name expansion. When arglist is NULL, perform
7195 * here document expansion.
7196 */
7197static void
7198expandarg(union node *arg, struct arglist *arglist, int flag)
7199{
7200 struct strlist *sp;
7201 char *p;
7202
7203 argbackq = arg->narg.backquote;
7204 STARTSTACKSTR(expdest);
7205 ifsfirst.next = NULL;
7206 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007207 argstr(arg->narg.text, flag,
7208 /* var_str_list: */ arglist ? arglist->list : NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007209 p = _STPUTC('\0', expdest);
7210 expdest = p - 1;
7211 if (arglist == NULL) {
7212 return; /* here document expanded */
7213 }
7214 p = grabstackstr(p);
7215 exparg.lastp = &exparg.list;
7216 /*
7217 * TODO - EXP_REDIR
7218 */
7219 if (flag & EXP_FULL) {
7220 ifsbreakup(p, &exparg);
7221 *exparg.lastp = NULL;
7222 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007223 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007224 } else {
7225 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
Denys Vlasenkob6c84342009-08-29 20:23:20 +02007226 rmescapes(p, 0);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007227 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007228 sp->text = p;
7229 *exparg.lastp = sp;
7230 exparg.lastp = &sp->next;
7231 }
7232 if (ifsfirst.next)
7233 ifsfree();
7234 *exparg.lastp = NULL;
7235 if (exparg.list) {
7236 *arglist->lastp = exparg.list;
7237 arglist->lastp = exparg.lastp;
7238 }
7239}
7240
7241/*
7242 * Expand shell variables and backquotes inside a here document.
7243 */
7244static void
7245expandhere(union node *arg, int fd)
7246{
7247 herefd = fd;
7248 expandarg(arg, (struct arglist *)NULL, 0);
7249 full_write(fd, stackblock(), expdest - (char *)stackblock());
7250}
7251
7252/*
7253 * Returns true if the pattern matches the string.
7254 */
7255static int
7256patmatch(char *pattern, const char *string)
7257{
7258 return pmatch(preglob(pattern, 0, 0), string);
7259}
7260
7261/*
7262 * See if a pattern matches in a case statement.
7263 */
7264static int
7265casematch(union node *pattern, char *val)
7266{
7267 struct stackmark smark;
7268 int result;
7269
7270 setstackmark(&smark);
7271 argbackq = pattern->narg.backquote;
7272 STARTSTACKSTR(expdest);
7273 ifslastp = NULL;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007274 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
7275 /* var_str_list: */ NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007276 STACKSTRNUL(expdest);
7277 result = patmatch(stackblock(), val);
7278 popstackmark(&smark);
7279 return result;
7280}
7281
7282
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007283/* ============ find_command */
7284
7285struct builtincmd {
7286 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007287 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007288 /* unsigned flags; */
7289};
7290#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00007291/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007292 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007293#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007294#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007295
7296struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007297 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007298 union param {
7299 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007300 /* index >= 0 for commands without path (slashes) */
7301 /* (TODO: what exactly does the value mean? PATH position?) */
7302 /* index == -1 for commands with slashes */
7303 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007304 const struct builtincmd *cmd;
7305 struct funcnode *func;
7306 } u;
7307};
7308/* values of cmdtype */
7309#define CMDUNKNOWN -1 /* no entry in table for command */
7310#define CMDNORMAL 0 /* command is an executable program */
7311#define CMDFUNCTION 1 /* command is a shell function */
7312#define CMDBUILTIN 2 /* command is a shell builtin */
7313
7314/* action to find_command() */
7315#define DO_ERR 0x01 /* prints errors */
7316#define DO_ABS 0x02 /* checks absolute paths */
7317#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
7318#define DO_ALTPATH 0x08 /* using alternate path */
7319#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
7320
7321static void find_command(char *, struct cmdentry *, int, const char *);
7322
7323
7324/* ============ Hashing commands */
7325
7326/*
7327 * When commands are first encountered, they are entered in a hash table.
7328 * This ensures that a full path search will not have to be done for them
7329 * on each invocation.
7330 *
7331 * We should investigate converting to a linear search, even though that
7332 * would make the command name "hash" a misnomer.
7333 */
7334
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007335struct tblentry {
7336 struct tblentry *next; /* next entry in hash chain */
7337 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007338 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007339 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007340 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007341};
7342
Denis Vlasenko01631112007-12-16 17:20:38 +00007343static struct tblentry **cmdtable;
7344#define INIT_G_cmdtable() do { \
7345 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
7346} while (0)
7347
7348static int builtinloc = -1; /* index in path of %builtin, or -1 */
7349
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007350
7351static void
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007352tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007353{
7354 int repeated = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007355
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007356#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007357 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00007358 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02007359 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00007360 while (*envp)
7361 putenv(*envp++);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007362 run_applet_no_and_exit(applet_no, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00007363 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007364 /* re-exec ourselves with the new arguments */
7365 execve(bb_busybox_exec_path, argv, envp);
7366 /* If they called chroot or otherwise made the binary no longer
7367 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007368 }
7369#endif
7370
7371 repeat:
7372#ifdef SYSV
7373 do {
7374 execve(cmd, argv, envp);
7375 } while (errno == EINTR);
7376#else
7377 execve(cmd, argv, envp);
7378#endif
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007379 if (repeated) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007380 free(argv);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007381 return;
7382 }
7383 if (errno == ENOEXEC) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007384 char **ap;
7385 char **new;
7386
7387 for (ap = argv; *ap; ap++)
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007388 continue;
7389 ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007390 ap[1] = cmd;
Denis Vlasenkoc44ab012007-04-09 03:11:58 +00007391 ap[0] = cmd = (char *)DEFAULT_SHELL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007392 ap += 2;
7393 argv++;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007394 while ((*ap++ = *argv++) != NULL)
Denis Vlasenko597906c2008-02-20 16:38:54 +00007395 continue;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007396 argv = new;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007397 repeated++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007398 goto repeat;
7399 }
7400}
7401
7402/*
7403 * Exec a program. Never returns. If you change this routine, you may
7404 * have to change the find_command routine as well.
7405 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007406static void shellexec(char **, const char *, int) NORETURN;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007407static void
7408shellexec(char **argv, const char *path, int idx)
7409{
7410 char *cmdname;
7411 int e;
7412 char **envp;
7413 int exerrno;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007414#if ENABLE_FEATURE_SH_STANDALONE
7415 int applet_no = -1;
7416#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007417
Denis Vlasenko34c73c42008-08-16 11:48:02 +00007418 clearredir(/*drop:*/ 1);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +02007419 envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL);
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007420 if (strchr(argv[0], '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00007421#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00007422 || (applet_no = find_applet_by_name(argv[0])) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007423#endif
7424 ) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007425 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007426 e = errno;
7427 } else {
7428 e = ENOENT;
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007429 while ((cmdname = path_advance(&path, argv[0])) != NULL) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007430 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00007431 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007432 if (errno != ENOENT && errno != ENOTDIR)
7433 e = errno;
7434 }
7435 stunalloc(cmdname);
7436 }
7437 }
7438
7439 /* Map to POSIX errors */
7440 switch (e) {
7441 case EACCES:
7442 exerrno = 126;
7443 break;
7444 case ENOENT:
7445 exerrno = 127;
7446 break;
7447 default:
7448 exerrno = 2;
7449 break;
7450 }
7451 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007452 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
7453 argv[0], e, suppress_int));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007454 ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
7455 /* NOTREACHED */
7456}
7457
7458static void
7459printentry(struct tblentry *cmdp)
7460{
7461 int idx;
7462 const char *path;
7463 char *name;
7464
7465 idx = cmdp->param.index;
7466 path = pathval();
7467 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007468 name = path_advance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007469 stunalloc(name);
7470 } while (--idx >= 0);
7471 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
7472}
7473
7474/*
7475 * Clear out command entries. The argument specifies the first entry in
7476 * PATH which has changed.
7477 */
7478static void
7479clearcmdentry(int firstchange)
7480{
7481 struct tblentry **tblp;
7482 struct tblentry **pp;
7483 struct tblentry *cmdp;
7484
7485 INT_OFF;
7486 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
7487 pp = tblp;
7488 while ((cmdp = *pp) != NULL) {
7489 if ((cmdp->cmdtype == CMDNORMAL &&
7490 cmdp->param.index >= firstchange)
7491 || (cmdp->cmdtype == CMDBUILTIN &&
7492 builtinloc >= firstchange)
7493 ) {
7494 *pp = cmdp->next;
7495 free(cmdp);
7496 } else {
7497 pp = &cmdp->next;
7498 }
7499 }
7500 }
7501 INT_ON;
7502}
7503
7504/*
7505 * Locate a command in the command hash table. If "add" is nonzero,
7506 * add the command to the table if it is not already present. The
7507 * variable "lastcmdentry" is set to point to the address of the link
7508 * pointing to the entry, so that delete_cmd_entry can delete the
7509 * entry.
7510 *
7511 * Interrupts must be off if called with add != 0.
7512 */
7513static struct tblentry **lastcmdentry;
7514
7515static struct tblentry *
7516cmdlookup(const char *name, int add)
7517{
7518 unsigned int hashval;
7519 const char *p;
7520 struct tblentry *cmdp;
7521 struct tblentry **pp;
7522
7523 p = name;
7524 hashval = (unsigned char)*p << 4;
7525 while (*p)
7526 hashval += (unsigned char)*p++;
7527 hashval &= 0x7FFF;
7528 pp = &cmdtable[hashval % CMDTABLESIZE];
7529 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7530 if (strcmp(cmdp->cmdname, name) == 0)
7531 break;
7532 pp = &cmdp->next;
7533 }
7534 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007535 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
7536 + strlen(name)
7537 /* + 1 - already done because
7538 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00007539 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007540 cmdp->cmdtype = CMDUNKNOWN;
7541 strcpy(cmdp->cmdname, name);
7542 }
7543 lastcmdentry = pp;
7544 return cmdp;
7545}
7546
7547/*
7548 * Delete the command entry returned on the last lookup.
7549 */
7550static void
7551delete_cmd_entry(void)
7552{
7553 struct tblentry *cmdp;
7554
7555 INT_OFF;
7556 cmdp = *lastcmdentry;
7557 *lastcmdentry = cmdp->next;
7558 if (cmdp->cmdtype == CMDFUNCTION)
7559 freefunc(cmdp->param.func);
7560 free(cmdp);
7561 INT_ON;
7562}
7563
7564/*
7565 * Add a new command entry, replacing any existing command entry for
7566 * the same name - except special builtins.
7567 */
7568static void
7569addcmdentry(char *name, struct cmdentry *entry)
7570{
7571 struct tblentry *cmdp;
7572
7573 cmdp = cmdlookup(name, 1);
7574 if (cmdp->cmdtype == CMDFUNCTION) {
7575 freefunc(cmdp->param.func);
7576 }
7577 cmdp->cmdtype = entry->cmdtype;
7578 cmdp->param = entry->u;
7579 cmdp->rehash = 0;
7580}
7581
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007582static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007583hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007584{
7585 struct tblentry **pp;
7586 struct tblentry *cmdp;
7587 int c;
7588 struct cmdentry entry;
7589 char *name;
7590
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007591 if (nextopt("r") != '\0') {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007592 clearcmdentry(0);
7593 return 0;
7594 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007595
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007596 if (*argptr == NULL) {
7597 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7598 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
7599 if (cmdp->cmdtype == CMDNORMAL)
7600 printentry(cmdp);
7601 }
7602 }
7603 return 0;
7604 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007605
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007606 c = 0;
7607 while ((name = *argptr) != NULL) {
7608 cmdp = cmdlookup(name, 0);
7609 if (cmdp != NULL
7610 && (cmdp->cmdtype == CMDNORMAL
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007611 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
7612 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007613 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007614 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007615 find_command(name, &entry, DO_ERR, pathval());
7616 if (entry.cmdtype == CMDUNKNOWN)
7617 c = 1;
7618 argptr++;
7619 }
7620 return c;
7621}
7622
7623/*
7624 * Called when a cd is done. Marks all commands so the next time they
7625 * are executed they will be rehashed.
7626 */
7627static void
7628hashcd(void)
7629{
7630 struct tblentry **pp;
7631 struct tblentry *cmdp;
7632
7633 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
7634 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007635 if (cmdp->cmdtype == CMDNORMAL
7636 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02007637 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007638 && builtinloc > 0)
7639 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007640 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007641 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007642 }
7643 }
7644}
7645
7646/*
7647 * Fix command hash table when PATH changed.
7648 * Called before PATH is changed. The argument is the new value of PATH;
7649 * pathval() still returns the old value at this point.
7650 * Called with interrupts off.
7651 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007652static void FAST_FUNC
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007653changepath(const char *new)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007654{
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007655 const char *old;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007656 int firstchange;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007657 int idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007658 int idx_bltin;
7659
7660 old = pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007661 firstchange = 9999; /* assume no change */
7662 idx = 0;
7663 idx_bltin = -1;
7664 for (;;) {
7665 if (*old != *new) {
7666 firstchange = idx;
7667 if ((*old == '\0' && *new == ':')
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007668 || (*old == ':' && *new == '\0')
7669 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007670 firstchange++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007671 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007672 old = new; /* ignore subsequent differences */
7673 }
7674 if (*new == '\0')
7675 break;
7676 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
7677 idx_bltin = idx;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00007678 if (*new == ':')
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007679 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02007680 new++;
7681 old++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007682 }
7683 if (builtinloc < 0 && idx_bltin >= 0)
7684 builtinloc = idx_bltin; /* zap builtins */
7685 if (builtinloc >= 0 && idx_bltin < 0)
7686 firstchange = 0;
7687 clearcmdentry(firstchange);
7688 builtinloc = idx_bltin;
7689}
7690
7691#define TEOF 0
7692#define TNL 1
7693#define TREDIR 2
7694#define TWORD 3
7695#define TSEMI 4
7696#define TBACKGND 5
7697#define TAND 6
7698#define TOR 7
7699#define TPIPE 8
7700#define TLP 9
7701#define TRP 10
7702#define TENDCASE 11
7703#define TENDBQUOTE 12
7704#define TNOT 13
7705#define TCASE 14
7706#define TDO 15
7707#define TDONE 16
7708#define TELIF 17
7709#define TELSE 18
7710#define TESAC 19
7711#define TFI 20
7712#define TFOR 21
7713#define TIF 22
7714#define TIN 23
7715#define TTHEN 24
7716#define TUNTIL 25
7717#define TWHILE 26
7718#define TBEGIN 27
7719#define TEND 28
Denis Vlasenkob07a4962008-06-22 13:16:23 +00007720typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007721
7722/* first char is indicating which tokens mark the end of a list */
7723static const char *const tokname_array[] = {
7724 "\1end of file",
7725 "\0newline",
7726 "\0redirection",
7727 "\0word",
7728 "\0;",
7729 "\0&",
7730 "\0&&",
7731 "\0||",
7732 "\0|",
7733 "\0(",
7734 "\1)",
7735 "\1;;",
7736 "\1`",
7737#define KWDOFFSET 13
7738 /* the following are keywords */
7739 "\0!",
7740 "\0case",
7741 "\1do",
7742 "\1done",
7743 "\1elif",
7744 "\1else",
7745 "\1esac",
7746 "\1fi",
7747 "\0for",
7748 "\0if",
7749 "\0in",
7750 "\1then",
7751 "\0until",
7752 "\0while",
7753 "\0{",
7754 "\1}",
7755};
7756
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007757/* Wrapper around strcmp for qsort/bsearch/... */
7758static int
7759pstrcmp(const void *a, const void *b)
7760{
Denis Vlasenko240a1cf2007-04-08 16:07:02 +00007761 return strcmp((char*) a, (*(char**) b) + 1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007762}
7763
7764static const char *const *
7765findkwd(const char *s)
7766{
7767 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00007768 ARRAY_SIZE(tokname_array) - KWDOFFSET,
7769 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007770}
7771
7772/*
7773 * Locate and print what a word is...
7774 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007775static int
7776describe_command(char *command, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007777{
7778 struct cmdentry entry;
7779 struct tblentry *cmdp;
7780#if ENABLE_ASH_ALIAS
7781 const struct alias *ap;
7782#endif
7783 const char *path = pathval();
7784
7785 if (describe_command_verbose) {
7786 out1str(command);
7787 }
7788
7789 /* First look at the keywords */
7790 if (findkwd(command)) {
7791 out1str(describe_command_verbose ? " is a shell keyword" : command);
7792 goto out;
7793 }
7794
7795#if ENABLE_ASH_ALIAS
7796 /* Then look at the aliases */
7797 ap = lookupalias(command, 0);
7798 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007799 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007800 out1str("alias ");
7801 printalias(ap);
7802 return 0;
7803 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00007804 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007805 goto out;
7806 }
7807#endif
7808 /* Then check if it is a tracked alias */
7809 cmdp = cmdlookup(command, 0);
7810 if (cmdp != NULL) {
7811 entry.cmdtype = cmdp->cmdtype;
7812 entry.u = cmdp->param;
7813 } else {
7814 /* Finally use brute force */
7815 find_command(command, &entry, DO_ABS, path);
7816 }
7817
7818 switch (entry.cmdtype) {
7819 case CMDNORMAL: {
7820 int j = entry.u.index;
7821 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00007822 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007823 p = command;
7824 } else {
7825 do {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02007826 p = path_advance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007827 stunalloc(p);
7828 } while (--j >= 0);
7829 }
7830 if (describe_command_verbose) {
7831 out1fmt(" is%s %s",
7832 (cmdp ? " a tracked alias for" : nullstr), p
7833 );
7834 } else {
7835 out1str(p);
7836 }
7837 break;
7838 }
7839
7840 case CMDFUNCTION:
7841 if (describe_command_verbose) {
7842 out1str(" is a shell function");
7843 } else {
7844 out1str(command);
7845 }
7846 break;
7847
7848 case CMDBUILTIN:
7849 if (describe_command_verbose) {
7850 out1fmt(" is a %sshell builtin",
7851 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
7852 "special " : nullstr
7853 );
7854 } else {
7855 out1str(command);
7856 }
7857 break;
7858
7859 default:
7860 if (describe_command_verbose) {
7861 out1str(": not found\n");
7862 }
7863 return 127;
7864 }
7865 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01007866 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007867 return 0;
7868}
7869
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007870static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007871typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007872{
Denis Vlasenko46846e22007-05-20 13:08:31 +00007873 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007874 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00007875 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007876
Denis Vlasenko46846e22007-05-20 13:08:31 +00007877 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00007878 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007879 i++;
7880 verbose = 0;
7881 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00007882 while (argv[i]) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00007883 err |= describe_command(argv[i++], verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007884 }
7885 return err;
7886}
7887
7888#if ENABLE_ASH_CMDCMD
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02007889static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007890commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007891{
7892 int c;
7893 enum {
7894 VERIFY_BRIEF = 1,
7895 VERIFY_VERBOSE = 2,
7896 } verify = 0;
7897
7898 while ((c = nextopt("pvV")) != '\0')
7899 if (c == 'V')
7900 verify |= VERIFY_VERBOSE;
7901 else if (c == 'v')
7902 verify |= VERIFY_BRIEF;
7903#if DEBUG
7904 else if (c != 'p')
7905 abort();
7906#endif
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007907 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
7908 if (verify && (*argptr != NULL)) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007909 return describe_command(*argptr, verify - VERIFY_BRIEF);
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00007910 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007911
7912 return 0;
7913}
7914#endif
7915
7916
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007917/* ============ eval.c */
Eric Andersencb57d552001-06-28 07:25:16 +00007918
Denis Vlasenko340299a2008-11-21 10:36:36 +00007919static int funcblocksize; /* size of structures in function */
7920static int funcstringsize; /* size of strings in node */
7921static void *funcblock; /* block to allocate function from */
7922static char *funcstring; /* block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007923
Eric Andersencb57d552001-06-28 07:25:16 +00007924/* flags in argument to evaltree */
Denis Vlasenko340299a2008-11-21 10:36:36 +00007925#define EV_EXIT 01 /* exit after evaluating tree */
7926#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Eric Andersenc470f442003-07-28 09:56:35 +00007927#define EV_BACKCMD 04 /* command executing within back quotes */
Eric Andersencb57d552001-06-28 07:25:16 +00007928
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02007929static const uint8_t nodesize[N_NUMBER] = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00007930 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
7931 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
7932 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
7933 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
7934 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
7935 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
7936 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
7937 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
7938 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
7939 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
7940 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
7941 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
7942 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
7943 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
7944 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
7945 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
7946 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007947#if ENABLE_ASH_BASH_COMPAT
Denis Vlasenko340299a2008-11-21 10:36:36 +00007948 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00007949#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00007950 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
7951 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
7952 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
7953 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
7954 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7955 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
7956 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7957 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
7958 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00007959};
7960
7961static void calcsize(union node *n);
7962
7963static void
7964sizenodelist(struct nodelist *lp)
7965{
7966 while (lp) {
7967 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
7968 calcsize(lp->n);
7969 lp = lp->next;
7970 }
7971}
7972
7973static void
7974calcsize(union node *n)
7975{
7976 if (n == NULL)
7977 return;
7978 funcblocksize += nodesize[n->type];
7979 switch (n->type) {
7980 case NCMD:
7981 calcsize(n->ncmd.redirect);
7982 calcsize(n->ncmd.args);
7983 calcsize(n->ncmd.assign);
7984 break;
7985 case NPIPE:
7986 sizenodelist(n->npipe.cmdlist);
7987 break;
7988 case NREDIR:
7989 case NBACKGND:
7990 case NSUBSHELL:
7991 calcsize(n->nredir.redirect);
7992 calcsize(n->nredir.n);
7993 break;
7994 case NAND:
7995 case NOR:
7996 case NSEMI:
7997 case NWHILE:
7998 case NUNTIL:
7999 calcsize(n->nbinary.ch2);
8000 calcsize(n->nbinary.ch1);
8001 break;
8002 case NIF:
8003 calcsize(n->nif.elsepart);
8004 calcsize(n->nif.ifpart);
8005 calcsize(n->nif.test);
8006 break;
8007 case NFOR:
8008 funcstringsize += strlen(n->nfor.var) + 1;
8009 calcsize(n->nfor.body);
8010 calcsize(n->nfor.args);
8011 break;
8012 case NCASE:
8013 calcsize(n->ncase.cases);
8014 calcsize(n->ncase.expr);
8015 break;
8016 case NCLIST:
8017 calcsize(n->nclist.body);
8018 calcsize(n->nclist.pattern);
8019 calcsize(n->nclist.next);
8020 break;
8021 case NDEFUN:
8022 case NARG:
8023 sizenodelist(n->narg.backquote);
8024 funcstringsize += strlen(n->narg.text) + 1;
8025 calcsize(n->narg.next);
8026 break;
8027 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008028#if ENABLE_ASH_BASH_COMPAT
8029 case NTO2:
8030#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008031 case NCLOBBER:
8032 case NFROM:
8033 case NFROMTO:
8034 case NAPPEND:
8035 calcsize(n->nfile.fname);
8036 calcsize(n->nfile.next);
8037 break;
8038 case NTOFD:
8039 case NFROMFD:
8040 calcsize(n->ndup.vname);
8041 calcsize(n->ndup.next);
8042 break;
8043 case NHERE:
8044 case NXHERE:
8045 calcsize(n->nhere.doc);
8046 calcsize(n->nhere.next);
8047 break;
8048 case NNOT:
8049 calcsize(n->nnot.com);
8050 break;
8051 };
8052}
8053
8054static char *
8055nodeckstrdup(char *s)
8056{
8057 char *rtn = funcstring;
8058
8059 strcpy(funcstring, s);
8060 funcstring += strlen(s) + 1;
8061 return rtn;
8062}
8063
8064static union node *copynode(union node *);
8065
8066static struct nodelist *
8067copynodelist(struct nodelist *lp)
8068{
8069 struct nodelist *start;
8070 struct nodelist **lpp;
8071
8072 lpp = &start;
8073 while (lp) {
8074 *lpp = funcblock;
8075 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8076 (*lpp)->n = copynode(lp->n);
8077 lp = lp->next;
8078 lpp = &(*lpp)->next;
8079 }
8080 *lpp = NULL;
8081 return start;
8082}
8083
8084static union node *
8085copynode(union node *n)
8086{
8087 union node *new;
8088
8089 if (n == NULL)
8090 return NULL;
8091 new = funcblock;
8092 funcblock = (char *) funcblock + nodesize[n->type];
8093
8094 switch (n->type) {
8095 case NCMD:
8096 new->ncmd.redirect = copynode(n->ncmd.redirect);
8097 new->ncmd.args = copynode(n->ncmd.args);
8098 new->ncmd.assign = copynode(n->ncmd.assign);
8099 break;
8100 case NPIPE:
8101 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008102 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008103 break;
8104 case NREDIR:
8105 case NBACKGND:
8106 case NSUBSHELL:
8107 new->nredir.redirect = copynode(n->nredir.redirect);
8108 new->nredir.n = copynode(n->nredir.n);
8109 break;
8110 case NAND:
8111 case NOR:
8112 case NSEMI:
8113 case NWHILE:
8114 case NUNTIL:
8115 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8116 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8117 break;
8118 case NIF:
8119 new->nif.elsepart = copynode(n->nif.elsepart);
8120 new->nif.ifpart = copynode(n->nif.ifpart);
8121 new->nif.test = copynode(n->nif.test);
8122 break;
8123 case NFOR:
8124 new->nfor.var = nodeckstrdup(n->nfor.var);
8125 new->nfor.body = copynode(n->nfor.body);
8126 new->nfor.args = copynode(n->nfor.args);
8127 break;
8128 case NCASE:
8129 new->ncase.cases = copynode(n->ncase.cases);
8130 new->ncase.expr = copynode(n->ncase.expr);
8131 break;
8132 case NCLIST:
8133 new->nclist.body = copynode(n->nclist.body);
8134 new->nclist.pattern = copynode(n->nclist.pattern);
8135 new->nclist.next = copynode(n->nclist.next);
8136 break;
8137 case NDEFUN:
8138 case NARG:
8139 new->narg.backquote = copynodelist(n->narg.backquote);
8140 new->narg.text = nodeckstrdup(n->narg.text);
8141 new->narg.next = copynode(n->narg.next);
8142 break;
8143 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008144#if ENABLE_ASH_BASH_COMPAT
8145 case NTO2:
8146#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008147 case NCLOBBER:
8148 case NFROM:
8149 case NFROMTO:
8150 case NAPPEND:
8151 new->nfile.fname = copynode(n->nfile.fname);
8152 new->nfile.fd = n->nfile.fd;
8153 new->nfile.next = copynode(n->nfile.next);
8154 break;
8155 case NTOFD:
8156 case NFROMFD:
8157 new->ndup.vname = copynode(n->ndup.vname);
8158 new->ndup.dupfd = n->ndup.dupfd;
8159 new->ndup.fd = n->ndup.fd;
8160 new->ndup.next = copynode(n->ndup.next);
8161 break;
8162 case NHERE:
8163 case NXHERE:
8164 new->nhere.doc = copynode(n->nhere.doc);
8165 new->nhere.fd = n->nhere.fd;
8166 new->nhere.next = copynode(n->nhere.next);
8167 break;
8168 case NNOT:
8169 new->nnot.com = copynode(n->nnot.com);
8170 break;
8171 };
8172 new->type = n->type;
8173 return new;
8174}
8175
8176/*
8177 * Make a copy of a parse tree.
8178 */
8179static struct funcnode *
8180copyfunc(union node *n)
8181{
8182 struct funcnode *f;
8183 size_t blocksize;
8184
8185 funcblocksize = offsetof(struct funcnode, n);
8186 funcstringsize = 0;
8187 calcsize(n);
8188 blocksize = funcblocksize;
8189 f = ckmalloc(blocksize + funcstringsize);
8190 funcblock = (char *) f + offsetof(struct funcnode, n);
8191 funcstring = (char *) f + blocksize;
8192 copynode(n);
8193 f->count = 0;
8194 return f;
8195}
8196
8197/*
8198 * Define a shell function.
8199 */
8200static void
8201defun(char *name, union node *func)
8202{
8203 struct cmdentry entry;
8204
8205 INT_OFF;
8206 entry.cmdtype = CMDFUNCTION;
8207 entry.u.func = copyfunc(func);
8208 addcmdentry(name, &entry);
8209 INT_ON;
8210}
8211
Denis Vlasenko4b875702009-03-19 13:30:04 +00008212/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008213#define SKIPBREAK (1 << 0)
8214#define SKIPCONT (1 << 1)
8215#define SKIPFUNC (1 << 2)
8216#define SKIPFILE (1 << 3)
8217#define SKIPEVAL (1 << 4)
Denis Vlasenko4b875702009-03-19 13:30:04 +00008218static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008219static int skipcount; /* number of levels to skip */
8220static int funcnest; /* depth of function calls */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00008221static int loopnest; /* current loop nesting level */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008222
Denis Vlasenko4b875702009-03-19 13:30:04 +00008223/* Forward decl way out to parsing code - dotrap needs it */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008224static int evalstring(char *s, int mask);
8225
Denis Vlasenko4b875702009-03-19 13:30:04 +00008226/* Called to execute a trap.
8227 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02008228 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00008229 *
8230 * Perhaps we should avoid entering new trap handlers
8231 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008232 */
8233static int
8234dotrap(void)
8235{
Denis Vlasenko4b875702009-03-19 13:30:04 +00008236 uint8_t *g;
8237 int sig;
8238 uint8_t savestatus;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008239
8240 savestatus = exitstatus;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008241 pending_sig = 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008242 xbarrier();
8243
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008244 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008245 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
8246 int want_exexit;
8247 char *t;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008248
Denis Vlasenko4b875702009-03-19 13:30:04 +00008249 if (*g == 0)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008250 continue;
Denis Vlasenko4b875702009-03-19 13:30:04 +00008251 t = trap[sig];
8252 /* non-trapped SIGINT is handled separately by raise_interrupt,
8253 * don't upset it by resetting gotsig[SIGINT-1] */
8254 if (sig == SIGINT && !t)
8255 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008256
8257 TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008258 *g = 0;
8259 if (!t)
8260 continue;
8261 want_exexit = evalstring(t, SKIPEVAL);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008262 exitstatus = savestatus;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008263 if (want_exexit) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008264 TRACE(("dotrap returns %d\n", want_exexit));
Denis Vlasenko4b875702009-03-19 13:30:04 +00008265 return want_exexit;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008266 }
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008267 }
8268
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008269 TRACE(("dotrap returns 0\n"));
Denis Vlasenko991a1da2008-02-10 19:02:53 +00008270 return 0;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00008271}
8272
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008273/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008274static void evalloop(union node *, int);
8275static void evalfor(union node *, int);
8276static void evalcase(union node *, int);
8277static void evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008278static void expredir(union node *);
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008279static void evalpipe(union node *, int);
8280static void evalcommand(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008281static int evalbltin(const struct builtincmd *, int, char **);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008282static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00008283
Eric Andersen62483552001-07-10 06:09:16 +00008284/*
Eric Andersenc470f442003-07-28 09:56:35 +00008285 * Evaluate a parse tree. The value is left in the global variable
8286 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00008287 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008288static void
Eric Andersenc470f442003-07-28 09:56:35 +00008289evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00008290{
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008291 struct jmploc *volatile savehandler = exception_handler;
8292 struct jmploc jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00008293 int checkexit = 0;
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008294 void (*evalfn)(union node *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008295 int status;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008296 int int_level;
8297
8298 SAVE_INT(int_level);
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008299
Eric Andersenc470f442003-07-28 09:56:35 +00008300 if (n == NULL) {
8301 TRACE(("evaltree(NULL) called\n"));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008302 goto out1;
Eric Andersen62483552001-07-10 06:09:16 +00008303 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008304 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008305
8306 exception_handler = &jmploc;
8307 {
8308 int err = setjmp(jmploc.loc);
8309 if (err) {
8310 /* if it was a signal, check for trap handlers */
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008311 if (exception_type == EXSIG) {
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008312 TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
8313 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008314 goto out;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008315 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008316 /* continue on the way out */
Denis Vlasenkob21f3792009-03-19 23:09:58 +00008317 TRACE(("exception %d in evaltree, propagating err=%d\n",
8318 exception_type, err));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008319 exception_handler = savehandler;
8320 longjmp(exception_handler->loc, err);
8321 }
8322 }
8323
Eric Andersenc470f442003-07-28 09:56:35 +00008324 switch (n->type) {
8325 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00008326#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00008327 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01008328 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00008329 break;
8330#endif
8331 case NNOT:
8332 evaltree(n->nnot.com, EV_TESTED);
8333 status = !exitstatus;
8334 goto setstatus;
8335 case NREDIR:
8336 expredir(n->nredir.redirect);
8337 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
8338 if (!status) {
8339 evaltree(n->nredir.n, flags & EV_TESTED);
8340 status = exitstatus;
8341 }
Denis Vlasenko34c73c42008-08-16 11:48:02 +00008342 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
Eric Andersenc470f442003-07-28 09:56:35 +00008343 goto setstatus;
8344 case NCMD:
8345 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008346 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00008347 if (eflag && !(flags & EV_TESTED))
8348 checkexit = ~0;
8349 goto calleval;
8350 case NFOR:
8351 evalfn = evalfor;
8352 goto calleval;
8353 case NWHILE:
8354 case NUNTIL:
8355 evalfn = evalloop;
8356 goto calleval;
8357 case NSUBSHELL:
8358 case NBACKGND:
8359 evalfn = evalsubshell;
8360 goto calleval;
8361 case NPIPE:
8362 evalfn = evalpipe;
8363 goto checkexit;
8364 case NCASE:
8365 evalfn = evalcase;
8366 goto calleval;
8367 case NAND:
8368 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008369 case NSEMI: {
8370
Eric Andersenc470f442003-07-28 09:56:35 +00008371#if NAND + 1 != NOR
8372#error NAND + 1 != NOR
8373#endif
8374#if NOR + 1 != NSEMI
8375#error NOR + 1 != NSEMI
8376#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00008377 unsigned is_or = n->type - NAND;
Eric Andersenc470f442003-07-28 09:56:35 +00008378 evaltree(
8379 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008380 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00008381 );
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008382 if (!exitstatus == is_or)
Eric Andersenc470f442003-07-28 09:56:35 +00008383 break;
8384 if (!evalskip) {
8385 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008386 evaln:
Eric Andersenc470f442003-07-28 09:56:35 +00008387 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008388 calleval:
Eric Andersenc470f442003-07-28 09:56:35 +00008389 evalfn(n, flags);
8390 break;
8391 }
8392 break;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008393 }
Eric Andersenc470f442003-07-28 09:56:35 +00008394 case NIF:
8395 evaltree(n->nif.test, EV_TESTED);
8396 if (evalskip)
8397 break;
8398 if (exitstatus == 0) {
8399 n = n->nif.ifpart;
8400 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008401 }
8402 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00008403 n = n->nif.elsepart;
8404 goto evaln;
8405 }
8406 goto success;
8407 case NDEFUN:
8408 defun(n->narg.text, n->narg.next);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008409 success:
Eric Andersenc470f442003-07-28 09:56:35 +00008410 status = 0;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008411 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00008412 exitstatus = status;
8413 break;
8414 }
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008415
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008416 out:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008417 exception_handler = savehandler;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008418
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008419 out1:
Denys Vlasenkob563f622010-09-25 17:15:13 +02008420 /* Order of checks below is important:
8421 * signal handlers trigger before exit caused by "set -e".
8422 */
8423 if (pending_sig && dotrap())
8424 goto exexit;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00008425 if (checkexit & exitstatus)
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008426 evalskip |= SKIPEVAL;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008427
8428 if (flags & EV_EXIT) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008429 exexit:
Denis Vlasenkob012b102007-02-19 22:43:01 +00008430 raise_exception(EXEXIT);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00008431 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00008432
8433 RESTORE_INT(int_level);
8434 TRACE(("leaving evaltree (no interrupts)\n"));
Eric Andersen62483552001-07-10 06:09:16 +00008435}
8436
Eric Andersenc470f442003-07-28 09:56:35 +00008437#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
8438static
8439#endif
8440void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
8441
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008442static void
Eric Andersenc470f442003-07-28 09:56:35 +00008443evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008444{
8445 int status;
8446
8447 loopnest++;
8448 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008449 flags &= EV_TESTED;
Eric Andersencb57d552001-06-28 07:25:16 +00008450 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +00008451 int i;
8452
Eric Andersencb57d552001-06-28 07:25:16 +00008453 evaltree(n->nbinary.ch1, EV_TESTED);
8454 if (evalskip) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008455 skipping:
8456 if (evalskip == SKIPCONT && --skipcount <= 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008457 evalskip = 0;
8458 continue;
8459 }
8460 if (evalskip == SKIPBREAK && --skipcount <= 0)
8461 evalskip = 0;
8462 break;
8463 }
Eric Andersenc470f442003-07-28 09:56:35 +00008464 i = exitstatus;
8465 if (n->type != NWHILE)
8466 i = !i;
8467 if (i != 0)
8468 break;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008469 evaltree(n->nbinary.ch2, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008470 status = exitstatus;
8471 if (evalskip)
8472 goto skipping;
8473 }
8474 loopnest--;
8475 exitstatus = status;
8476}
8477
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008478static void
Eric Andersenc470f442003-07-28 09:56:35 +00008479evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008480{
8481 struct arglist arglist;
8482 union node *argp;
8483 struct strlist *sp;
8484 struct stackmark smark;
8485
8486 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008487 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008488 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008489 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008490 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
Eric Andersenc470f442003-07-28 09:56:35 +00008491 /* XXX */
Eric Andersencb57d552001-06-28 07:25:16 +00008492 if (evalskip)
8493 goto out;
8494 }
8495 *arglist.lastp = NULL;
8496
8497 exitstatus = 0;
8498 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008499 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008500 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008501 setvar(n->nfor.var, sp->text, 0);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008502 evaltree(n->nfor.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00008503 if (evalskip) {
8504 if (evalskip == SKIPCONT && --skipcount <= 0) {
8505 evalskip = 0;
8506 continue;
8507 }
8508 if (evalskip == SKIPBREAK && --skipcount <= 0)
8509 evalskip = 0;
8510 break;
8511 }
8512 }
8513 loopnest--;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008514 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008515 popstackmark(&smark);
8516}
8517
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008518static void
Eric Andersenc470f442003-07-28 09:56:35 +00008519evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008520{
8521 union node *cp;
8522 union node *patp;
8523 struct arglist arglist;
8524 struct stackmark smark;
8525
8526 setstackmark(&smark);
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008527 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00008528 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00008529 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Eric Andersenc470f442003-07-28 09:56:35 +00008530 exitstatus = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008531 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
8532 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00008533 if (casematch(patp, arglist.list->text)) {
8534 if (evalskip == 0) {
8535 evaltree(cp->nclist.body, flags);
8536 }
8537 goto out;
8538 }
8539 }
8540 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008541 out:
Eric Andersencb57d552001-06-28 07:25:16 +00008542 popstackmark(&smark);
8543}
8544
Eric Andersenc470f442003-07-28 09:56:35 +00008545/*
8546 * Kick off a subshell to evaluate a tree.
8547 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008548static void
Eric Andersenc470f442003-07-28 09:56:35 +00008549evalsubshell(union node *n, int flags)
8550{
8551 struct job *jp;
8552 int backgnd = (n->type == NBACKGND);
8553 int status;
8554
8555 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02008556 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00008557 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008558 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008559 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008560 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02008561 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00008562 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008563 flags |= EV_EXIT;
8564 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02008565 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00008566 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00008567 redirect(n->nredir.redirect, 0);
8568 evaltreenr(n->nredir.n, flags);
8569 /* never returns */
8570 }
8571 status = 0;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008572 if (!backgnd)
Eric Andersenc470f442003-07-28 09:56:35 +00008573 status = waitforjob(jp);
8574 exitstatus = status;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008575 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00008576}
8577
Eric Andersenc470f442003-07-28 09:56:35 +00008578/*
8579 * Compute the names of the files in a redirection list.
8580 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00008581static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00008582static void
8583expredir(union node *n)
8584{
8585 union node *redir;
8586
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008587 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00008588 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008589
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00008590 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00008591 fn.lastp = &fn.list;
8592 switch (redir->type) {
8593 case NFROMTO:
8594 case NFROM:
8595 case NTO:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008596#if ENABLE_ASH_BASH_COMPAT
8597 case NTO2:
8598#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008599 case NCLOBBER:
8600 case NAPPEND:
8601 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko559691a2008-10-05 18:39:31 +00008602#if ENABLE_ASH_BASH_COMPAT
8603 store_expfname:
8604#endif
Eric Andersenc470f442003-07-28 09:56:35 +00008605 redir->nfile.expfname = fn.list->text;
8606 break;
8607 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00008608 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00008609 if (redir->ndup.vname) {
8610 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008611 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00008612 ash_msg_and_raise_error("redir error");
Denis Vlasenko559691a2008-10-05 18:39:31 +00008613#if ENABLE_ASH_BASH_COMPAT
8614//FIXME: we used expandarg with different args!
8615 if (!isdigit_str9(fn.list->text)) {
8616 /* >&file, not >&fd */
8617 if (redir->nfile.fd != 1) /* 123>&file - BAD */
8618 ash_msg_and_raise_error("redir error");
8619 redir->type = NTO2;
8620 goto store_expfname;
8621 }
8622#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008623 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00008624 }
8625 break;
8626 }
8627 }
8628}
8629
Eric Andersencb57d552001-06-28 07:25:16 +00008630/*
Eric Andersencb57d552001-06-28 07:25:16 +00008631 * Evaluate a pipeline. All the processes in the pipeline are children
8632 * of the process creating the pipeline. (This differs from some versions
8633 * of the shell, which make the last process in a pipeline the parent
8634 * of all the rest.)
8635 */
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02008636static void
Eric Andersenc470f442003-07-28 09:56:35 +00008637evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00008638{
8639 struct job *jp;
8640 struct nodelist *lp;
8641 int pipelen;
8642 int prevfd;
8643 int pip[2];
8644
Eric Andersenc470f442003-07-28 09:56:35 +00008645 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00008646 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008647 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00008648 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008649 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00008650 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008651 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00008652 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00008653 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008654 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00008655 pip[1] = -1;
8656 if (lp->next) {
8657 if (pipe(pip) < 0) {
8658 close(prevfd);
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00008659 ash_msg_and_raise_error("pipe call failed");
Eric Andersencb57d552001-06-28 07:25:16 +00008660 }
8661 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008662 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00008663 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008664 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008665 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00008666 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00008667 if (prevfd > 0) {
8668 dup2(prevfd, 0);
8669 close(prevfd);
8670 }
8671 if (pip[1] > 1) {
8672 dup2(pip[1], 1);
8673 close(pip[1]);
8674 }
Eric Andersenc470f442003-07-28 09:56:35 +00008675 evaltreenr(lp->n, flags);
8676 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00008677 }
8678 if (prevfd >= 0)
8679 close(prevfd);
8680 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00008681 /* Don't want to trigger debugging */
8682 if (pip[1] != -1)
8683 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00008684 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008685 if (n->npipe.pipe_backgnd == 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00008686 exitstatus = waitforjob(jp);
8687 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
Eric Andersencb57d552001-06-28 07:25:16 +00008688 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00008689 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00008690}
8691
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008692/*
8693 * Controls whether the shell is interactive or not.
8694 */
8695static void
8696setinteractive(int on)
8697{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008698 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008699
8700 if (++on == is_interactive)
8701 return;
8702 is_interactive = on;
8703 setsignal(SIGINT);
8704 setsignal(SIGQUIT);
8705 setsignal(SIGTERM);
8706#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8707 if (is_interactive > 1) {
8708 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008709 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008710
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008711 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008712 /* note: ash and hush share this string */
8713 out1fmt("\n\n%s %s\n"
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008714 "Enter 'help' for a list of built-in commands."
8715 "\n\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02008716 bb_banner,
8717 "built-in shell (ash)"
8718 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00008719 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008720 }
8721 }
8722#endif
8723}
8724
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008725static void
8726optschanged(void)
8727{
8728#if DEBUG
8729 opentrace();
8730#endif
8731 setinteractive(iflag);
8732 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008733#if ENABLE_FEATURE_EDITING_VI
8734 if (viflag)
8735 line_input_state->flags |= VI_MODE;
8736 else
8737 line_input_state->flags &= ~VI_MODE;
8738#else
8739 viflag = 0; /* forcibly keep the option off */
8740#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00008741}
8742
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008743static struct localvar *localvars;
8744
8745/*
8746 * Called after a function returns.
8747 * Interrupts must be off.
8748 */
8749static void
8750poplocalvars(void)
8751{
8752 struct localvar *lvp;
8753 struct var *vp;
8754
8755 while ((lvp = localvars) != NULL) {
8756 localvars = lvp->next;
8757 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02008758 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008759 if (vp == NULL) { /* $- saved */
8760 memcpy(optlist, lvp->text, sizeof(optlist));
8761 free((char*)lvp->text);
8762 optschanged();
8763 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008764 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008765 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008766 if (vp->var_func)
8767 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008768 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008769 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008771 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008772 }
8773 free(lvp);
8774 }
8775}
8776
8777static int
8778evalfun(struct funcnode *func, int argc, char **argv, int flags)
8779{
8780 volatile struct shparam saveparam;
8781 struct localvar *volatile savelocalvars;
8782 struct jmploc *volatile savehandler;
8783 struct jmploc jmploc;
8784 int e;
8785
8786 saveparam = shellparam;
8787 savelocalvars = localvars;
8788 e = setjmp(jmploc.loc);
8789 if (e) {
8790 goto funcdone;
8791 }
8792 INT_OFF;
8793 savehandler = exception_handler;
8794 exception_handler = &jmploc;
8795 localvars = NULL;
Denis Vlasenko01631112007-12-16 17:20:38 +00008796 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008797 func->count++;
8798 funcnest++;
8799 INT_ON;
8800 shellparam.nparam = argc - 1;
8801 shellparam.p = argv + 1;
8802#if ENABLE_ASH_GETOPTS
8803 shellparam.optind = 1;
8804 shellparam.optoff = -1;
8805#endif
8806 evaltree(&func->n, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00008807 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008808 INT_OFF;
8809 funcnest--;
8810 freefunc(func);
8811 poplocalvars();
8812 localvars = savelocalvars;
8813 freeparam(&shellparam);
8814 shellparam = saveparam;
8815 exception_handler = savehandler;
8816 INT_ON;
8817 evalskip &= ~SKIPFUNC;
8818 return e;
8819}
8820
Denis Vlasenko131ae172007-02-18 13:00:19 +00008821#if ENABLE_ASH_CMDCMD
Denis Vlasenkoaa744452007-02-23 01:04:22 +00008822static char **
8823parse_command_args(char **argv, const char **path)
Eric Andersenc470f442003-07-28 09:56:35 +00008824{
8825 char *cp, c;
8826
8827 for (;;) {
8828 cp = *++argv;
8829 if (!cp)
8830 return 0;
8831 if (*cp++ != '-')
8832 break;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00008833 c = *cp++;
8834 if (!c)
Eric Andersenc470f442003-07-28 09:56:35 +00008835 break;
8836 if (c == '-' && !*cp) {
8837 argv++;
8838 break;
8839 }
8840 do {
8841 switch (c) {
8842 case 'p':
Denis Vlasenkof5f75c52007-06-12 22:35:19 +00008843 *path = bb_default_path;
Eric Andersenc470f442003-07-28 09:56:35 +00008844 break;
8845 default:
8846 /* run 'typecmd' for other options */
8847 return 0;
8848 }
Denis Vlasenko9650f362007-02-23 01:04:37 +00008849 c = *cp++;
8850 } while (c);
Eric Andersenc470f442003-07-28 09:56:35 +00008851 }
8852 return argv;
8853}
8854#endif
8855
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008856/*
8857 * Make a variable a local variable. When a variable is made local, it's
8858 * value and flags are saved in a localvar structure. The saved values
8859 * will be restored when the shell function returns. We handle the name
8860 * "-" as a special case.
8861 */
8862static void
8863mklocal(char *name)
8864{
8865 struct localvar *lvp;
8866 struct var **vpp;
8867 struct var *vp;
8868
8869 INT_OFF;
Denis Vlasenko838ffd52008-02-21 04:32:08 +00008870 lvp = ckzalloc(sizeof(struct localvar));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008871 if (LONE_DASH(name)) {
8872 char *p;
8873 p = ckmalloc(sizeof(optlist));
8874 lvp->text = memcpy(p, optlist, sizeof(optlist));
8875 vp = NULL;
8876 } else {
8877 char *eq;
8878
8879 vpp = hashvar(name);
8880 vp = *findvar(vpp, name);
8881 eq = strchr(name, '=');
8882 if (vp == NULL) {
8883 if (eq)
8884 setvareq(name, VSTRFIXED);
8885 else
8886 setvar(name, NULL, VSTRFIXED);
8887 vp = *vpp; /* the new variable */
8888 lvp->flags = VUNSET;
8889 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02008890 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008891 lvp->flags = vp->flags;
8892 vp->flags |= VSTRFIXED|VTEXTFIXED;
8893 if (eq)
8894 setvareq(name, 0);
8895 }
8896 }
8897 lvp->vp = vp;
8898 lvp->next = localvars;
8899 localvars = lvp;
8900 INT_ON;
8901}
8902
8903/*
8904 * The "local" command.
8905 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008906static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008907localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008908{
8909 char *name;
8910
8911 argv = argptr;
8912 while ((name = *argv++) != NULL) {
8913 mklocal(name);
8914 }
8915 return 0;
8916}
8917
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008918static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008919falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008920{
8921 return 1;
8922}
8923
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008924static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008925truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008926{
8927 return 0;
8928}
8929
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008930static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008931execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008932{
Denis Vlasenko68404f12008-03-17 09:00:54 +00008933 if (argv[1]) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008934 iflag = 0; /* exit on error */
8935 mflag = 0;
8936 optschanged();
8937 shellexec(argv + 1, pathval(), 0);
8938 }
8939 return 0;
8940}
8941
8942/*
8943 * The return command.
8944 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008945static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008946returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00008947{
8948 /*
8949 * If called outside a function, do what ksh does;
8950 * skip the rest of the file.
8951 */
8952 evalskip = funcnest ? SKIPFUNC : SKIPFILE;
8953 return argv[1] ? number(argv[1]) : exitstatus;
8954}
8955
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008956/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008957static int breakcmd(int, char **) FAST_FUNC;
8958static int dotcmd(int, char **) FAST_FUNC;
8959static int evalcmd(int, char **) FAST_FUNC;
8960static int exitcmd(int, char **) FAST_FUNC;
8961static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008962#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008963static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008964#endif
Denis Vlasenko52764022007-02-24 13:42:56 +00008965#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008966static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00008967#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00008968#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008969static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008970#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008971static int readcmd(int, char **) FAST_FUNC;
8972static int setcmd(int, char **) FAST_FUNC;
8973static int shiftcmd(int, char **) FAST_FUNC;
8974static int timescmd(int, char **) FAST_FUNC;
8975static int trapcmd(int, char **) FAST_FUNC;
8976static int umaskcmd(int, char **) FAST_FUNC;
8977static int unsetcmd(int, char **) FAST_FUNC;
8978static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00008979
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008980#define BUILTIN_NOSPEC "0"
8981#define BUILTIN_SPECIAL "1"
8982#define BUILTIN_REGULAR "2"
8983#define BUILTIN_SPEC_REG "3"
8984#define BUILTIN_ASSIGN "4"
8985#define BUILTIN_SPEC_ASSG "5"
8986#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00008987#define BUILTIN_SPEC_REG_ASSG "7"
8988
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008989/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008990#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008991static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008992#endif
8993#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008994static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008995#endif
8996#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008997static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02008998#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00008999
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009000/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009001static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009002 { BUILTIN_SPEC_REG "." , dotcmd },
9003 { BUILTIN_SPEC_REG ":" , truecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009004#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009005 { BUILTIN_REGULAR "[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009006#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009007 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009008#endif
Denis Vlasenko80591b02008-03-25 07:49:43 +00009009#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009010#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009011 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009012#endif
9013#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009014 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009015#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009016 { BUILTIN_SPEC_REG "break" , breakcmd },
9017 { BUILTIN_REGULAR "cd" , cdcmd },
9018 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009019#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009020 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009021#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009022 { BUILTIN_SPEC_REG "continue", breakcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009023#if ENABLE_ASH_BUILTIN_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009024 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009025#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009026 { BUILTIN_SPEC_REG "eval" , evalcmd },
9027 { BUILTIN_SPEC_REG "exec" , execcmd },
9028 { BUILTIN_SPEC_REG "exit" , exitcmd },
9029 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
9030 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009031#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009032 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009033#endif
9034#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009035 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009036#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009037 { BUILTIN_NOSPEC "hash" , hashcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009038#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009039 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009040#endif
9041#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009042 { BUILTIN_REGULAR "jobs" , jobscmd },
9043 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009044#endif
Mike Frysinger98c52642009-04-02 10:02:37 +00009045#if ENABLE_SH_MATH_SUPPORT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009046 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009047#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009048 { BUILTIN_ASSIGN "local" , localcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009049#if ENABLE_ASH_BUILTIN_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009050 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +00009051#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009052 { BUILTIN_NOSPEC "pwd" , pwdcmd },
9053 { BUILTIN_REGULAR "read" , readcmd },
9054 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
9055 { BUILTIN_SPEC_REG "return" , returncmd },
9056 { BUILTIN_SPEC_REG "set" , setcmd },
9057 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009058#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009059 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +02009060#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009061#if ENABLE_ASH_BUILTIN_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009062 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009063#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009064 { BUILTIN_SPEC_REG "times" , timescmd },
9065 { BUILTIN_SPEC_REG "trap" , trapcmd },
9066 { BUILTIN_REGULAR "true" , truecmd },
9067 { BUILTIN_NOSPEC "type" , typecmd },
9068 { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
9069 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009070#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009071 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009072#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009073 { BUILTIN_SPEC_REG "unset" , unsetcmd },
9074 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009075};
9076
Denis Vlasenko80591b02008-03-25 07:49:43 +00009077/* Should match the above table! */
9078#define COMMANDCMD (builtintab + \
9079 2 + \
9080 1 * ENABLE_ASH_BUILTIN_TEST + \
9081 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9082 1 * ENABLE_ASH_ALIAS + \
9083 1 * ENABLE_ASH_JOB_CONTROL + \
9084 3)
9085#define EXECCMD (builtintab + \
9086 2 + \
9087 1 * ENABLE_ASH_BUILTIN_TEST + \
9088 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
9089 1 * ENABLE_ASH_ALIAS + \
9090 1 * ENABLE_ASH_JOB_CONTROL + \
9091 3 + \
9092 1 * ENABLE_ASH_CMDCMD + \
9093 1 + \
9094 ENABLE_ASH_BUILTIN_ECHO + \
9095 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009096
9097/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009098 * Search the table of builtin commands.
9099 */
9100static struct builtincmd *
9101find_builtin(const char *name)
9102{
9103 struct builtincmd *bp;
9104
9105 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +00009106 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009107 pstrcmp
9108 );
9109 return bp;
9110}
9111
9112/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009113 * Execute a simple command.
9114 */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009115static int
9116isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +00009117{
9118 const char *q = endofname(p);
9119 if (p == q)
9120 return 0;
9121 return *q == '=';
9122}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009123static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009124bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009125{
9126 /* Preserve exitstatus of a previous possible redirection
9127 * as POSIX mandates */
9128 return back_exitstatus;
9129}
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02009130static void
Eric Andersenc470f442003-07-28 09:56:35 +00009131evalcommand(union node *cmd, int flags)
9132{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009133 static const struct builtincmd null_bltin = {
9134 "\0\0", bltincmd /* why three NULs? */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009135 };
Eric Andersenc470f442003-07-28 09:56:35 +00009136 struct stackmark smark;
9137 union node *argp;
9138 struct arglist arglist;
9139 struct arglist varlist;
9140 char **argv;
9141 int argc;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009142 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +00009143 struct cmdentry cmdentry;
9144 struct job *jp;
9145 char *lastarg;
9146 const char *path;
9147 int spclbltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009148 int status;
9149 char **nargv;
Paul Foxc3850c82005-07-20 18:23:39 +00009150 struct builtincmd *bcmd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009151 smallint cmd_is_exec;
9152 smallint pseudovarflag = 0;
Eric Andersenc470f442003-07-28 09:56:35 +00009153
9154 /* First expand the arguments. */
9155 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
9156 setstackmark(&smark);
9157 back_exitstatus = 0;
9158
9159 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009160 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +00009161 varlist.lastp = &varlist.list;
9162 *varlist.lastp = NULL;
9163 arglist.lastp = &arglist.list;
9164 *arglist.lastp = NULL;
9165
9166 argc = 0;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009167 if (cmd->ncmd.args) {
Paul Foxc3850c82005-07-20 18:23:39 +00009168 bcmd = find_builtin(cmd->ncmd.args->narg.text);
9169 pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
9170 }
9171
Eric Andersenc470f442003-07-28 09:56:35 +00009172 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
9173 struct strlist **spp;
9174
9175 spp = arglist.lastp;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +00009176 if (pseudovarflag && isassignment(argp->narg.text))
Paul Foxc3850c82005-07-20 18:23:39 +00009177 expandarg(argp, &arglist, EXP_VARTILDE);
9178 else
9179 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
9180
Eric Andersenc470f442003-07-28 09:56:35 +00009181 for (sp = *spp; sp; sp = sp->next)
9182 argc++;
9183 }
9184
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009185 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009186 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009187 TRACE(("evalcommand arg: %s\n", sp->text));
9188 *nargv++ = sp->text;
9189 }
9190 *nargv = NULL;
9191
9192 lastarg = NULL;
9193 if (iflag && funcnest == 0 && argc > 0)
9194 lastarg = nargv[-1];
9195
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009196 preverrout_fd = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009197 expredir(cmd->ncmd.redirect);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009198 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +00009199
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009200 path = vpath.var_text;
Eric Andersenc470f442003-07-28 09:56:35 +00009201 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
9202 struct strlist **spp;
9203 char *p;
9204
9205 spp = varlist.lastp;
9206 expandarg(argp, &varlist, EXP_VARTILDE);
9207
9208 /*
9209 * Modify the command lookup path, if a PATH= assignment
9210 * is present
9211 */
9212 p = (*spp)->text;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009213 if (varcmp(p, path) == 0)
Eric Andersenc470f442003-07-28 09:56:35 +00009214 path = p;
9215 }
9216
9217 /* Print the command if xflag is set. */
9218 if (xflag) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009219 int n;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009220 const char *p = " %s" + 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009221
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009222 fdprintf(preverrout_fd, p, expandstr(ps4val()));
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009223 sp = varlist.list;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009224 for (n = 0; n < 2; n++) {
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009225 while (sp) {
Denis Vlasenko0de37e12007-10-17 11:08:53 +00009226 fdprintf(preverrout_fd, p, sp->text);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009227 sp = sp->next;
Denys Vlasenkofd33e172010-06-26 22:55:44 +02009228 p = " %s";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009229 }
9230 sp = arglist.list;
9231 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00009232 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009233 }
9234
9235 cmd_is_exec = 0;
9236 spclbltin = -1;
9237
9238 /* Now locate the command. */
9239 if (argc) {
9240 const char *oldpath;
9241 int cmd_flag = DO_ERR;
9242
9243 path += 5;
9244 oldpath = path;
9245 for (;;) {
9246 find_command(argv[0], &cmdentry, cmd_flag, path);
9247 if (cmdentry.cmdtype == CMDUNKNOWN) {
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009248 flush_stdout_stderr();
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009249 status = 127;
Eric Andersenc470f442003-07-28 09:56:35 +00009250 goto bail;
9251 }
9252
9253 /* implement bltin and command here */
9254 if (cmdentry.cmdtype != CMDBUILTIN)
9255 break;
9256 if (spclbltin < 0)
9257 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
9258 if (cmdentry.u.cmd == EXECCMD)
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009259 cmd_is_exec = 1;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009260#if ENABLE_ASH_CMDCMD
Eric Andersenc470f442003-07-28 09:56:35 +00009261 if (cmdentry.u.cmd == COMMANDCMD) {
Eric Andersenc470f442003-07-28 09:56:35 +00009262 path = oldpath;
9263 nargv = parse_command_args(argv, &path);
9264 if (!nargv)
9265 break;
9266 argc -= nargv - argv;
9267 argv = nargv;
9268 cmd_flag |= DO_NOFUNC;
9269 } else
9270#endif
9271 break;
9272 }
9273 }
9274
9275 if (status) {
9276 /* We have a redirection error. */
9277 if (spclbltin > 0)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009278 raise_exception(EXERROR);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009279 bail:
Eric Andersenc470f442003-07-28 09:56:35 +00009280 exitstatus = status;
9281 goto out;
9282 }
9283
9284 /* Execute the command. */
9285 switch (cmdentry.cmdtype) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009286 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009287
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009288#if ENABLE_FEATURE_SH_NOFORK
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009289/* (1) BUG: if variables are set, we need to fork, or save/restore them
9290 * around run_nofork_applet() call.
9291 * (2) Should this check also be done in forkshell()?
9292 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
9293 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009294 /* find_command() encodes applet_no as (-2 - applet_no) */
9295 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009296 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009297 listsetvar(varlist.list, VEXPORT|VSTACK);
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00009298 /* run <applet>_main() */
9299 exitstatus = run_nofork_applet(applet_no, argv);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009300 break;
9301 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +00009302#endif
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009303 /* Can we avoid forking off? For example, very last command
9304 * in a script or a subshell does not need forking,
9305 * we can just exec it.
9306 */
Denys Vlasenko238bf182010-05-18 15:49:07 +02009307 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009308 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009309 INT_OFF;
Denis Vlasenko68404f12008-03-17 09:00:54 +00009310 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009311 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009312 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009313 exitstatus = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009314 INT_ON;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009315 TRACE(("forked child exited with %d\n", exitstatus));
Eric Andersenc470f442003-07-28 09:56:35 +00009316 break;
9317 }
Denys Vlasenko238bf182010-05-18 15:49:07 +02009318 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009319 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +02009320 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +00009321 }
9322 listsetvar(varlist.list, VEXPORT|VSTACK);
9323 shellexec(argv, path, cmdentry.u.index);
9324 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009325 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +00009326 case CMDBUILTIN:
9327 cmdenviron = varlist.list;
9328 if (cmdenviron) {
9329 struct strlist *list = cmdenviron;
9330 int i = VNOSET;
9331 if (spclbltin > 0 || argc == 0) {
9332 i = 0;
9333 if (cmd_is_exec && argc > 1)
9334 i = VEXPORT;
9335 }
9336 listsetvar(list, i);
9337 }
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009338 /* Tight loop with builtins only:
9339 * "while kill -0 $child; do true; done"
9340 * will never exit even if $child died, unless we do this
9341 * to reap the zombie and make kill detect that it's gone: */
9342 dowait(DOWAIT_NONBLOCK, NULL);
9343
Eric Andersenc470f442003-07-28 09:56:35 +00009344 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
9345 int exit_status;
Denis Vlasenko7f88e342009-03-19 03:36:18 +00009346 int i = exception_type;
Eric Andersenc470f442003-07-28 09:56:35 +00009347 if (i == EXEXIT)
9348 goto raise;
Eric Andersenc470f442003-07-28 09:56:35 +00009349 exit_status = 2;
Eric Andersenc470f442003-07-28 09:56:35 +00009350 if (i == EXINT)
Denis Vlasenko991a1da2008-02-10 19:02:53 +00009351 exit_status = 128 + SIGINT;
Eric Andersenc470f442003-07-28 09:56:35 +00009352 if (i == EXSIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009353 exit_status = 128 + pending_sig;
Eric Andersenc470f442003-07-28 09:56:35 +00009354 exitstatus = exit_status;
Eric Andersenc470f442003-07-28 09:56:35 +00009355 if (i == EXINT || spclbltin > 0) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009356 raise:
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009357 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009358 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009359 FORCE_INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009360 }
9361 break;
9362
9363 case CMDFUNCTION:
9364 listsetvar(varlist.list, 0);
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00009365 /* See above for the rationale */
9366 dowait(DOWAIT_NONBLOCK, NULL);
Eric Andersenc470f442003-07-28 09:56:35 +00009367 if (evalfun(cmdentry.u.func, argc, argv, flags))
9368 goto raise;
9369 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +02009370
9371 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +00009372
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009373 out:
Denis Vlasenko34c73c42008-08-16 11:48:02 +00009374 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009375 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +00009376 /* dsl: I think this is intended to be used to support
9377 * '_' in 'vi' command mode during line editing...
9378 * However I implemented that within libedit itself.
9379 */
9380 setvar("_", lastarg, 0);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +00009381 }
Eric Andersenc470f442003-07-28 09:56:35 +00009382 popstackmark(&smark);
9383}
9384
9385static int
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009386evalbltin(const struct builtincmd *cmd, int argc, char **argv)
9387{
Eric Andersenc470f442003-07-28 09:56:35 +00009388 char *volatile savecmdname;
9389 struct jmploc *volatile savehandler;
9390 struct jmploc jmploc;
9391 int i;
9392
9393 savecmdname = commandname;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009394 i = setjmp(jmploc.loc);
9395 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +00009396 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009397 savehandler = exception_handler;
9398 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +00009399 commandname = argv[0];
9400 argptr = argv + 1;
9401 optptr = NULL; /* initialize nextopt */
9402 exitstatus = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009403 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009404 cmddone:
Glenn L McGrath7b8765c2003-08-29 07:29:30 +00009405 exitstatus |= ferror(stdout);
Rob Landleyf296f0b2006-07-06 01:09:21 +00009406 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +00009407 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009408 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +00009409
9410 return i;
9411}
9412
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009413static int
9414goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009415{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02009416 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009417}
9418
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009419
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009420/*
9421 * Search for a command. This is called before we fork so that the
9422 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009423 * the child. The check for "goodname" is an overly conservative
9424 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009425 */
Eric Andersenc470f442003-07-28 09:56:35 +00009426static void
9427prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009428{
9429 struct cmdentry entry;
9430
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +00009431 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
9432 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009433}
9434
Eric Andersencb57d552001-06-28 07:25:16 +00009435
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009436/* ============ Builtin commands
9437 *
9438 * Builtin commands whose functions are closely tied to evaluation
9439 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +00009440 */
9441
9442/*
Eric Andersencb57d552001-06-28 07:25:16 +00009443 * Handle break and continue commands. Break, continue, and return are
9444 * all handled by setting the evalskip flag. The evaluation routines
9445 * above all check this flag, and if it is set they start skipping
9446 * commands rather than executing them. The variable skipcount is
9447 * the number of loops to break/continue, or the number of function
9448 * levels to return. (The latter is always 1.) It should probably
9449 * be an error to break out of more loops than exist, but it isn't
9450 * in the standard shell so we don't make it one here.
9451 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009452static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009453breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00009454{
Denis Vlasenko68404f12008-03-17 09:00:54 +00009455 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +00009456
Aaron Lehmann2aef3a62001-12-31 06:03:12 +00009457 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009458 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009459 if (n > loopnest)
9460 n = loopnest;
9461 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009462 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +00009463 skipcount = n;
9464 }
9465 return 0;
9466}
9467
Eric Andersenc470f442003-07-28 09:56:35 +00009468
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009469/* ============ input.c
9470 *
Eric Andersen90898442003-08-06 11:20:52 +00009471 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +00009472 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009473
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009474enum {
9475 INPUT_PUSH_FILE = 1,
9476 INPUT_NOFILE_OK = 2,
9477};
Eric Andersencb57d552001-06-28 07:25:16 +00009478
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009479static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009480/* values of checkkwd variable */
9481#define CHKALIAS 0x1
9482#define CHKKWD 0x2
9483#define CHKNL 0x4
9484
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009485/*
9486 * Push a string back onto the input at this current parsefile level.
9487 * We handle aliases this way.
9488 */
9489#if !ENABLE_ASH_ALIAS
9490#define pushstring(s, ap) pushstring(s)
9491#endif
9492static void
9493pushstring(char *s, struct alias *ap)
9494{
9495 struct strpush *sp;
9496 int len;
9497
9498 len = strlen(s);
9499 INT_OFF;
9500 if (g_parsefile->strpush) {
9501 sp = ckzalloc(sizeof(*sp));
9502 sp->prev = g_parsefile->strpush;
9503 } else {
9504 sp = &(g_parsefile->basestrpush);
9505 }
9506 g_parsefile->strpush = sp;
9507 sp->prev_string = g_parsefile->next_to_pgetc;
9508 sp->prev_left_in_line = g_parsefile->left_in_line;
9509#if ENABLE_ASH_ALIAS
9510 sp->ap = ap;
9511 if (ap) {
9512 ap->flag |= ALIASINUSE;
9513 sp->string = s;
9514 }
9515#endif
9516 g_parsefile->next_to_pgetc = s;
9517 g_parsefile->left_in_line = len;
9518 INT_ON;
9519}
9520
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009521static void
9522popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009523{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009524 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +00009525
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009526 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +00009527#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009528 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009529 if (g_parsefile->next_to_pgetc[-1] == ' '
9530 || g_parsefile->next_to_pgetc[-1] == '\t'
9531 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009532 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009533 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009534 if (sp->string != sp->ap->val) {
9535 free(sp->string);
9536 }
9537 sp->ap->flag &= ~ALIASINUSE;
9538 if (sp->ap->flag & ALIASDEAD) {
9539 unalias(sp->ap->name);
9540 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +00009541 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009542#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009543 g_parsefile->next_to_pgetc = sp->prev_string;
9544 g_parsefile->left_in_line = sp->prev_left_in_line;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009545 g_parsefile->strpush = sp->prev;
9546 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009547 free(sp);
9548 INT_ON;
9549}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009550
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009551//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
9552//it peeks whether it is &>, and then pushes back both chars.
9553//This function needs to save last *next_to_pgetc to buf[0]
9554//to make two pungetc() reliable. Currently,
9555// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
Denis Vlasenkoaa744452007-02-23 01:04:22 +00009556static int
9557preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009558{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009559 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00009560 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009561
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009562 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +00009563#if ENABLE_FEATURE_EDITING
Denis Vlasenko85c24712008-03-17 09:04:04 +00009564 retry:
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009565 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
9566 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009567 else {
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009568# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009569 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009570# endif
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02009571 nr = read_line_input(cmdedit_prompt, buf, IBUFSIZ, line_input_state);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009572 if (nr == 0) {
9573 /* Ctrl+C pressed */
9574 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009575 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009576 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +00009577 raise(SIGINT);
9578 return 1;
9579 }
Eric Andersenc470f442003-07-28 09:56:35 +00009580 goto retry;
9581 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +00009582 if (nr < 0 && errno == 0) {
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009583 /* Ctrl+D pressed */
Eric Andersenc470f442003-07-28 09:56:35 +00009584 nr = 0;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009585 }
Eric Andersencb57d552001-06-28 07:25:16 +00009586 }
9587#else
Denys Vlasenko161bb8f2010-06-06 05:07:11 +02009588 nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +00009589#endif
9590
Denys Vlasenko8c52f802011-02-04 17:36:21 +01009591#if 0 /* disabled: nonblock_safe_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +00009592 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +00009593 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +00009594 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +00009595 if (flags >= 0 && (flags & O_NONBLOCK)) {
9596 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +00009597 if (fcntl(0, F_SETFL, flags) >= 0) {
9598 out2str("sh: turning off NDELAY mode\n");
9599 goto retry;
9600 }
9601 }
9602 }
9603 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +00009604#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009605 return nr;
9606}
9607
9608/*
9609 * Refill the input buffer and return the next input character:
9610 *
9611 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009612 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
9613 * or we are reading from a string so we can't refill the buffer,
9614 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +02009615 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +00009616 * 4) Process input up to the next newline, deleting nul characters.
9617 */
Denis Vlasenko727752d2008-11-28 03:41:47 +00009618//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
9619#define pgetc_debug(...) ((void)0)
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009620static int
Eric Andersenc470f442003-07-28 09:56:35 +00009621preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009622{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009623 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +00009624 int more;
Eric Andersencb57d552001-06-28 07:25:16 +00009625
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009626 while (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +00009627#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009628 if (g_parsefile->left_in_line == -1
9629 && g_parsefile->strpush->ap
9630 && g_parsefile->next_to_pgetc[-1] != ' '
9631 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +00009632 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009633 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +00009634 return PEOA;
9635 }
Eric Andersen2870d962001-07-02 17:27:21 +00009636#endif
Eric Andersencb57d552001-06-28 07:25:16 +00009637 popstring();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009638 /* try "pgetc" now: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009639 pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
9640 g_parsefile->left_in_line,
9641 g_parsefile->next_to_pgetc,
9642 g_parsefile->next_to_pgetc);
9643 if (--g_parsefile->left_in_line >= 0)
9644 return (unsigned char)(*g_parsefile->next_to_pgetc++);
Eric Andersencb57d552001-06-28 07:25:16 +00009645 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009646 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009647 * "pgetc" needs refilling.
9648 */
9649
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009650 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009651 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +00009652 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009653 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009654 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009655 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009656 /* even in failure keep left_in_line and next_to_pgetc
9657 * in lock step, for correct multi-layer pungetc.
9658 * left_in_line was decremented before preadbuffer(),
9659 * must inc next_to_pgetc: */
9660 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009661 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009662 }
Eric Andersencb57d552001-06-28 07:25:16 +00009663
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009664 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009665 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009666 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009667 again:
9668 more = preadfd();
9669 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009670 /* don't try reading again */
9671 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +00009672 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009673 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009674 return PEOF;
9675 }
9676 }
9677
Denis Vlasenko727752d2008-11-28 03:41:47 +00009678 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009679 * Set g_parsefile->left_in_line
9680 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +00009681 * NUL chars are deleted.
9682 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009683 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009684 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009685 char c;
Eric Andersencb57d552001-06-28 07:25:16 +00009686
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009687 more--;
Eric Andersenc470f442003-07-28 09:56:35 +00009688
Denis Vlasenko727752d2008-11-28 03:41:47 +00009689 c = *q;
9690 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009691 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009692 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009693 q++;
9694 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009695 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009696 break;
9697 }
Eric Andersencb57d552001-06-28 07:25:16 +00009698 }
9699
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009700 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009701 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
9702 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +00009703 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009704 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009705 }
9706 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009707 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +00009708
Eric Andersencb57d552001-06-28 07:25:16 +00009709 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +00009710 char save = *q;
9711 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009712 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +00009713 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +00009714 }
9715
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009716 pgetc_debug("preadbuffer at %d:%p'%s'",
9717 g_parsefile->left_in_line,
9718 g_parsefile->next_to_pgetc,
9719 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +01009720 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +00009721}
9722
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009723#define pgetc_as_macro() \
9724 (--g_parsefile->left_in_line >= 0 \
Denys Vlasenkocd716832009-11-28 22:14:02 +01009725 ? (unsigned char)*g_parsefile->next_to_pgetc++ \
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009726 : preadbuffer() \
9727 )
Denis Vlasenko727752d2008-11-28 03:41:47 +00009728
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009729static int
9730pgetc(void)
9731{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009732 pgetc_debug("pgetc_fast at %d:%p'%s'",
9733 g_parsefile->left_in_line,
9734 g_parsefile->next_to_pgetc,
9735 g_parsefile->next_to_pgetc);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009736 return pgetc_as_macro();
9737}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009738
9739#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009740# define pgetc_fast() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009741#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009742# define pgetc_fast() pgetc_as_macro()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009743#endif
9744
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009745#if ENABLE_ASH_ALIAS
9746static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009747pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009748{
9749 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009750 do {
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009751 pgetc_debug("pgetc_fast at %d:%p'%s'",
9752 g_parsefile->left_in_line,
9753 g_parsefile->next_to_pgetc,
9754 g_parsefile->next_to_pgetc);
Denis Vlasenko834dee72008-10-07 09:18:30 +00009755 c = pgetc_fast();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009756 } while (c == PEOA);
9757 return c;
9758}
9759#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009760# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009761#endif
9762
9763/*
9764 * Read a line from the script.
9765 */
9766static char *
9767pfgets(char *line, int len)
9768{
9769 char *p = line;
9770 int nleft = len;
9771 int c;
9772
9773 while (--nleft > 0) {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01009774 c = pgetc_without_PEOA();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009775 if (c == PEOF) {
9776 if (p == line)
9777 return NULL;
9778 break;
9779 }
9780 *p++ = c;
9781 if (c == '\n')
9782 break;
9783 }
9784 *p = '\0';
9785 return line;
9786}
9787
Eric Andersenc470f442003-07-28 09:56:35 +00009788/*
9789 * Undo the last call to pgetc. Only one character may be pushed back.
9790 * PEOF may be pushed back.
9791 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009792static void
Eric Andersenc470f442003-07-28 09:56:35 +00009793pungetc(void)
9794{
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009795 g_parsefile->left_in_line++;
9796 g_parsefile->next_to_pgetc--;
9797 pgetc_debug("pushed back to %d:%p'%s'",
9798 g_parsefile->left_in_line,
9799 g_parsefile->next_to_pgetc,
9800 g_parsefile->next_to_pgetc);
Eric Andersencb57d552001-06-28 07:25:16 +00009801}
9802
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009803/*
9804 * To handle the "." command, a stack of input files is used. Pushfile
9805 * adds a new entry to the stack and popfile restores the previous level.
9806 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009807static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009808pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +00009809{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009810 struct parsefile *pf;
9811
Denis Vlasenko597906c2008-02-20 16:38:54 +00009812 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009813 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009814 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +00009815 /*pf->strpush = NULL; - ckzalloc did it */
9816 /*pf->basestrpush.prev = NULL;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009817 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009818}
9819
9820static void
9821popfile(void)
9822{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009823 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +00009824
Denis Vlasenkob012b102007-02-19 22:43:01 +00009825 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009826 if (pf->pf_fd >= 0)
9827 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +00009828 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009829 while (pf->strpush)
9830 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009831 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009832 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009833 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009834}
9835
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009836/*
9837 * Return to top level.
9838 */
9839static void
9840popallfiles(void)
9841{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009842 while (g_parsefile != &basepf)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009843 popfile();
9844}
9845
9846/*
9847 * Close the file(s) that the shell is reading commands from. Called
9848 * after a fork is done.
9849 */
9850static void
9851closescript(void)
9852{
9853 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009854 if (g_parsefile->pf_fd > 0) {
9855 close(g_parsefile->pf_fd);
9856 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009857 }
9858}
9859
9860/*
9861 * Like setinputfile, but takes an open file descriptor. Call this with
9862 * interrupts off.
9863 */
9864static void
9865setinputfd(int fd, int push)
9866{
Denis Vlasenko96e1b382007-09-30 23:50:48 +00009867 close_on_exec_on(fd);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009868 if (push) {
9869 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +00009870 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009871 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +02009872 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009873 if (g_parsefile->buf == NULL)
9874 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009875 g_parsefile->left_in_buffer = 0;
9876 g_parsefile->left_in_line = 0;
9877 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009878}
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009879
Eric Andersenc470f442003-07-28 09:56:35 +00009880/*
9881 * Set the input to take input from a file. If push is set, push the
9882 * old input onto the stack first.
9883 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009884static int
9885setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +00009886{
9887 int fd;
9888 int fd2;
9889
Denis Vlasenkob012b102007-02-19 22:43:01 +00009890 INT_OFF;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009891 fd = open(fname, O_RDONLY);
9892 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009893 if (flags & INPUT_NOFILE_OK)
9894 goto out;
Denis Vlasenko9604e1b2009-03-03 18:47:56 +00009895 ash_msg_and_raise_error("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009896 }
Eric Andersenc470f442003-07-28 09:56:35 +00009897 if (fd < 10) {
9898 fd2 = copyfd(fd, 10);
9899 close(fd);
9900 if (fd2 < 0)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00009901 ash_msg_and_raise_error("out of file descriptors");
Eric Andersenc470f442003-07-28 09:56:35 +00009902 fd = fd2;
9903 }
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009904 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009905 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +00009906 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +00009907 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +00009908}
9909
Eric Andersencb57d552001-06-28 07:25:16 +00009910/*
9911 * Like setinputfile, but takes input from a string.
9912 */
Eric Andersenc470f442003-07-28 09:56:35 +00009913static void
9914setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +00009915{
Denis Vlasenkob012b102007-02-19 22:43:01 +00009916 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00009917 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009918 g_parsefile->next_to_pgetc = string;
9919 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009920 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +00009921 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009922 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009923}
9924
9925
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009926/* ============ mail.c
9927 *
9928 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +00009929 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009930
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009931#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +00009932
Eric Andersencb57d552001-06-28 07:25:16 +00009933#define MAXMBOXES 10
9934
Eric Andersenc470f442003-07-28 09:56:35 +00009935/* times of mailboxes */
9936static time_t mailtime[MAXMBOXES];
9937/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009938static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +00009939
Eric Andersencb57d552001-06-28 07:25:16 +00009940/*
Eric Andersenc470f442003-07-28 09:56:35 +00009941 * Print appropriate message(s) if mail has arrived.
9942 * If mail_var_path_changed is set,
9943 * then the value of MAIL has mail_var_path_changed,
9944 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +00009945 */
Eric Andersenc470f442003-07-28 09:56:35 +00009946static void
9947chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +00009948{
Eric Andersencb57d552001-06-28 07:25:16 +00009949 const char *mpath;
9950 char *p;
9951 char *q;
Eric Andersenc470f442003-07-28 09:56:35 +00009952 time_t *mtp;
Eric Andersencb57d552001-06-28 07:25:16 +00009953 struct stackmark smark;
9954 struct stat statb;
9955
Eric Andersencb57d552001-06-28 07:25:16 +00009956 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +00009957 mpath = mpathset() ? mpathval() : mailval();
9958 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
Denys Vlasenko82a6fb32009-06-14 19:42:12 +02009959 p = path_advance(&mpath, nullstr);
Eric Andersencb57d552001-06-28 07:25:16 +00009960 if (p == NULL)
9961 break;
9962 if (*p == '\0')
9963 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009964 for (q = p; *q; q++)
9965 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009966#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +00009967 if (q[-1] != '/')
9968 abort();
9969#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009970 q[-1] = '\0'; /* delete trailing '/' */
9971 if (stat(p, &statb) < 0) {
9972 *mtp = 0;
9973 continue;
Eric Andersencb57d552001-06-28 07:25:16 +00009974 }
Eric Andersenc470f442003-07-28 09:56:35 +00009975 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
9976 fprintf(
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02009977 stderr, "%s\n",
Eric Andersenc470f442003-07-28 09:56:35 +00009978 pathopt ? pathopt : "you have mail"
9979 );
9980 }
9981 *mtp = statb.st_mtime;
Eric Andersencb57d552001-06-28 07:25:16 +00009982 }
Eric Andersenc470f442003-07-28 09:56:35 +00009983 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009984 popstackmark(&smark);
9985}
Eric Andersencb57d552001-06-28 07:25:16 +00009986
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009987static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009988changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +00009989{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00009990 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009991}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00009992
Denis Vlasenko131ae172007-02-18 13:00:19 +00009993#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +00009994
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009995
9996/* ============ ??? */
9997
Eric Andersencb57d552001-06-28 07:25:16 +00009998/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009999 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000010000 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010001static void
10002setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010003{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010004 char **newparam;
10005 char **ap;
10006 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000010007
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010008 for (nparam = 0; argv[nparam]; nparam++)
10009 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010010 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
10011 while (*argv) {
10012 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000010013 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010014 *ap = NULL;
10015 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000010016 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010017 shellparam.nparam = nparam;
10018 shellparam.p = newparam;
10019#if ENABLE_ASH_GETOPTS
10020 shellparam.optind = 1;
10021 shellparam.optoff = -1;
10022#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010023}
10024
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010025/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010026 * Process shell options. The global variable argptr contains a pointer
10027 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010028 *
10029 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
10030 * For a non-interactive shell, an error condition encountered
10031 * by a special built-in ... shall cause the shell to write a diagnostic message
10032 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000010033 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000010034 * ...
10035 * Utility syntax error (option or operand error) Shall exit
10036 * ...
10037 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
10038 * we see that bash does not do that (set "finishes" with error code 1 instead,
10039 * and shell continues), and people rely on this behavior!
10040 * Testcase:
10041 * set -o barfoo 2>/dev/null
10042 * echo $?
10043 *
10044 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000010045 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010046static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010047plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000010048{
10049 int i;
10050
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010051 if (name) {
10052 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010053 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000010054 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010055 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010056 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010057 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010058 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010059 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000010060 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010061 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010062 if (val) {
10063 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
10064 } else {
10065 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
10066 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010067 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010068 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000010069}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010070static void
10071setoption(int flag, int val)
10072{
10073 int i;
10074
10075 for (i = 0; i < NOPTS; i++) {
10076 if (optletters(i) == flag) {
10077 optlist[i] = val;
10078 return;
10079 }
10080 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010081 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010082 /* NOTREACHED */
10083}
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010084static int
Eric Andersenc470f442003-07-28 09:56:35 +000010085options(int cmdline)
Eric Andersencb57d552001-06-28 07:25:16 +000010086{
10087 char *p;
10088 int val;
10089 int c;
10090
10091 if (cmdline)
10092 minusc = NULL;
10093 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010094 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010095 if (c != '-' && c != '+')
10096 break;
10097 argptr++;
10098 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010099 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000010100 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010101 if (p[0] == '\0' || LONE_DASH(p)) {
Eric Andersen2870d962001-07-02 17:27:21 +000010102 if (!cmdline) {
10103 /* "-" means turn off -x and -v */
10104 if (p[0] == '\0')
10105 xflag = vflag = 0;
10106 /* "--" means reset params */
10107 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000010108 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000010109 }
Eric Andersenc470f442003-07-28 09:56:35 +000010110 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000010111 }
Eric Andersencb57d552001-06-28 07:25:16 +000010112 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010113 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000010114 while ((c = *p++) != '\0') {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010115 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
Eric Andersencb57d552001-06-28 07:25:16 +000010116 if (c == 'c' && cmdline) {
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010117 minusc = p; /* command is after shell args */
Eric Andersencb57d552001-06-28 07:25:16 +000010118 } else if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000010119 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010120 /* it already printed err message */
10121 return 1; /* error */
10122 }
Eric Andersencb57d552001-06-28 07:25:16 +000010123 if (*argptr)
10124 argptr++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000010125 } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
10126 isloginsh = 1;
10127 /* bash does not accept +-login, we also won't */
10128 } else if (cmdline && val && (c == '-')) { /* long options */
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010129 if (strcmp(p, "login") == 0)
Robert Griebl64f70cc2002-05-14 23:22:06 +000010130 isloginsh = 1;
10131 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010132 } else {
10133 setoption(c, val);
10134 }
10135 }
10136 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010137 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000010138}
10139
Eric Andersencb57d552001-06-28 07:25:16 +000010140/*
Eric Andersencb57d552001-06-28 07:25:16 +000010141 * The shift builtin command.
10142 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010143static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010144shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010145{
10146 int n;
10147 char **ap1, **ap2;
10148
10149 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000010150 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000010151 n = number(argv[1]);
10152 if (n > shellparam.nparam)
Denis Vlasenkoc90e1be2008-07-30 15:35:05 +000010153 n = 0; /* bash compat, was = shellparam.nparam; */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010154 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000010155 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010156 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000010157 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000010158 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000010159 }
10160 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010161 while ((*ap2++ = *ap1++) != NULL)
10162 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010163#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010164 shellparam.optind = 1;
10165 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010166#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000010167 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000010168 return 0;
10169}
10170
Eric Andersencb57d552001-06-28 07:25:16 +000010171/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010172 * POSIX requires that 'set' (but not export or readonly) output the
10173 * variables in lexicographic order - by the locale's collating order (sigh).
10174 * Maybe we could keep them in an ordered balanced binary tree
10175 * instead of hashed lists.
10176 * For now just roll 'em through qsort for printing...
10177 */
10178static int
10179showvars(const char *sep_prefix, int on, int off)
10180{
10181 const char *sep;
10182 char **ep, **epend;
10183
10184 ep = listvars(on, off, &epend);
10185 qsort(ep, epend - ep, sizeof(char *), vpcmp);
10186
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010187 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010188
10189 for (; ep < epend; ep++) {
10190 const char *p;
10191 const char *q;
10192
10193 p = strchrnul(*ep, '=');
10194 q = nullstr;
10195 if (*p)
10196 q = single_quote(++p);
10197 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
10198 }
10199 return 0;
10200}
10201
10202/*
Eric Andersencb57d552001-06-28 07:25:16 +000010203 * The set command builtin.
10204 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010205static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010206setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000010207{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010208 int retval;
10209
Denis Vlasenko68404f12008-03-17 09:00:54 +000010210 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000010211 return showvars(nullstr, 0, VUNSET);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010212 INT_OFF;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010213 retval = 1;
10214 if (!options(0)) { /* if no parse error... */
10215 retval = 0;
10216 optschanged();
10217 if (*argptr != NULL) {
10218 setparam(argptr);
10219 }
Eric Andersencb57d552001-06-28 07:25:16 +000010220 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000010221 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000010222 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000010223}
10224
Denis Vlasenko131ae172007-02-18 13:00:19 +000010225#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010226static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000010227change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000010228{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010229 uint32_t t;
10230
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010231 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000010232 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010233 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000010234 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010235 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000010236 vrandom.flags &= ~VNOFUNC;
10237 } else {
10238 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020010239 t = strtoul(value, NULL, 10);
10240 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000010241 }
Eric Andersenef02f822004-03-11 13:34:24 +000010242}
Eric Andersen16767e22004-03-16 05:14:10 +000010243#endif
10244
Denis Vlasenko131ae172007-02-18 13:00:19 +000010245#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000010246static int
Eric Andersenc470f442003-07-28 09:56:35 +000010247getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010248{
10249 char *p, *q;
10250 char c = '?';
10251 int done = 0;
10252 int err = 0;
Eric Andersena48b0a32003-10-22 10:56:47 +000010253 char s[12];
10254 char **optnext;
Eric Andersencb57d552001-06-28 07:25:16 +000010255
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010256 if (*param_optind < 1)
Eric Andersena48b0a32003-10-22 10:56:47 +000010257 return 1;
10258 optnext = optfirst + *param_optind - 1;
10259
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000010260 if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
Eric Andersencb57d552001-06-28 07:25:16 +000010261 p = NULL;
10262 else
Eric Andersena48b0a32003-10-22 10:56:47 +000010263 p = optnext[-1] + *optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000010264 if (p == NULL || *p == '\0') {
10265 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000010266 p = *optnext;
10267 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010268 atend:
Eric Andersencb57d552001-06-28 07:25:16 +000010269 p = NULL;
10270 done = 1;
10271 goto out;
10272 }
10273 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000010274 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000010275 goto atend;
10276 }
10277
10278 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000010279 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000010280 if (*q == '\0') {
10281 if (optstr[0] == ':') {
10282 s[0] = c;
10283 s[1] = '\0';
10284 err |= setvarsafe("OPTARG", s, 0);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010285 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010286 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010287 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010288 }
10289 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000010290 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010291 }
10292 if (*++q == ':')
10293 q++;
10294 }
10295
10296 if (*++q == ':') {
10297 if (*p == '\0' && (p = *optnext) == NULL) {
10298 if (optstr[0] == ':') {
10299 s[0] = c;
10300 s[1] = '\0';
10301 err |= setvarsafe("OPTARG", s, 0);
10302 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010303 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010304 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010305 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000010306 c = '?';
10307 }
Eric Andersenc470f442003-07-28 09:56:35 +000010308 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000010309 }
10310
10311 if (p == *optnext)
10312 optnext++;
Eric Andersenc470f442003-07-28 09:56:35 +000010313 err |= setvarsafe("OPTARG", p, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000010314 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010315 } else
Eric Andersenc470f442003-07-28 09:56:35 +000010316 err |= setvarsafe("OPTARG", nullstr, 0);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010317 out:
Eric Andersencb57d552001-06-28 07:25:16 +000010318 *optoff = p ? p - *(optnext - 1) : -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010319 *param_optind = optnext - optfirst + 1;
10320 fmtstr(s, sizeof(s), "%d", *param_optind);
Eric Andersencb57d552001-06-28 07:25:16 +000010321 err |= setvarsafe("OPTIND", s, VNOFUNC);
10322 s[0] = c;
10323 s[1] = '\0';
10324 err |= setvarsafe(optvar, s, 0);
10325 if (err) {
Eric Andersenc470f442003-07-28 09:56:35 +000010326 *param_optind = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010327 *optoff = -1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000010328 flush_stdout_stderr();
10329 raise_exception(EXERROR);
Eric Andersencb57d552001-06-28 07:25:16 +000010330 }
10331 return done;
10332}
Eric Andersenc470f442003-07-28 09:56:35 +000010333
10334/*
10335 * The getopts builtin. Shellparam.optnext points to the next argument
10336 * to be processed. Shellparam.optptr points to the next character to
10337 * be processed in the current argument. If shellparam.optnext is NULL,
10338 * then it's the first time getopts has been called.
10339 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010340static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000010341getoptscmd(int argc, char **argv)
10342{
10343 char **optbase;
10344
10345 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000010346 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010347 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000010348 optbase = shellparam.p;
10349 if (shellparam.optind > shellparam.nparam + 1) {
10350 shellparam.optind = 1;
10351 shellparam.optoff = -1;
10352 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010353 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010354 optbase = &argv[3];
10355 if (shellparam.optind > argc - 2) {
10356 shellparam.optind = 1;
10357 shellparam.optoff = -1;
10358 }
10359 }
10360
10361 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010362 &shellparam.optoff);
Eric Andersenc470f442003-07-28 09:56:35 +000010363}
Denis Vlasenko131ae172007-02-18 13:00:19 +000010364#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000010365
Eric Andersencb57d552001-06-28 07:25:16 +000010366
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010367/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000010368
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010369struct heredoc {
10370 struct heredoc *next; /* next here document in list */
10371 union node *here; /* redirection node */
10372 char *eofmark; /* string indicating end of input */
10373 smallint striptabs; /* if set, strip leading tabs */
10374};
10375
10376static smallint tokpushback; /* last token pushed back */
10377static smallint parsebackquote; /* nonzero if we are inside backquotes */
10378static smallint quoteflag; /* set if (part of) last token was quoted */
10379static token_id_t lasttoken; /* last token read (integer id Txxx) */
10380static struct heredoc *heredoclist; /* list of here documents to read */
10381static char *wordtext; /* text of last word returned by readtoken */
10382static struct nodelist *backquotelist;
10383static union node *redirnode;
10384static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010385
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010386static const char *
10387tokname(char *buf, int tok)
10388{
10389 if (tok < TSEMI)
10390 return tokname_array[tok] + 1;
10391 sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
10392 return buf;
10393}
10394
10395/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010396 * Called when an unexpected token is read during the parse. The argument
10397 * is the token that is expected, or -1 if more than one type of token can
10398 * occur at this point.
10399 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010400static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010401static void
10402raise_error_unexpected_syntax(int token)
10403{
10404 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010405 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010406 int l;
10407
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010408 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010409 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010410 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010411 raise_error_syntax(msg);
10412 /* NOTREACHED */
10413}
Eric Andersencb57d552001-06-28 07:25:16 +000010414
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010415#define EOFMARKLEN 79
Eric Andersencb57d552001-06-28 07:25:16 +000010416
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010417/* parsing is heavily cross-recursive, need these forward decls */
10418static union node *andor(void);
10419static union node *pipeline(void);
10420static union node *parse_command(void);
10421static void parseheredoc(void);
10422static char peektoken(void);
10423static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000010424
Eric Andersenc470f442003-07-28 09:56:35 +000010425static union node *
10426list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000010427{
10428 union node *n1, *n2, *n3;
10429 int tok;
10430
Eric Andersenc470f442003-07-28 09:56:35 +000010431 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10432 if (nlflag == 2 && peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010433 return NULL;
10434 n1 = NULL;
10435 for (;;) {
10436 n2 = andor();
10437 tok = readtoken();
10438 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000010439 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010440 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010441 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000010442 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010443 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000010444 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010445 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010446 n2 = n3;
10447 }
10448 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000010449 }
10450 }
10451 if (n1 == NULL) {
10452 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010453 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010454 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010455 n3->type = NSEMI;
10456 n3->nbinary.ch1 = n1;
10457 n3->nbinary.ch2 = n2;
10458 n1 = n3;
10459 }
10460 switch (tok) {
10461 case TBACKGND:
10462 case TSEMI:
10463 tok = readtoken();
10464 /* fall through */
10465 case TNL:
10466 if (tok == TNL) {
10467 parseheredoc();
Eric Andersenc470f442003-07-28 09:56:35 +000010468 if (nlflag == 1)
Eric Andersencb57d552001-06-28 07:25:16 +000010469 return n1;
10470 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010471 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010472 }
Eric Andersenc470f442003-07-28 09:56:35 +000010473 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Manuel Novoa III 16815d42001-08-10 19:36:07 +000010474 if (peektoken())
Eric Andersencb57d552001-06-28 07:25:16 +000010475 return n1;
10476 break;
10477 case TEOF:
10478 if (heredoclist)
10479 parseheredoc();
10480 else
Eric Andersenc470f442003-07-28 09:56:35 +000010481 pungetc(); /* push back EOF on input */
Eric Andersencb57d552001-06-28 07:25:16 +000010482 return n1;
10483 default:
Eric Andersenc470f442003-07-28 09:56:35 +000010484 if (nlflag == 1)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010485 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010486 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010487 return n1;
10488 }
10489 }
10490}
10491
Eric Andersenc470f442003-07-28 09:56:35 +000010492static union node *
10493andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010494{
Eric Andersencb57d552001-06-28 07:25:16 +000010495 union node *n1, *n2, *n3;
10496 int t;
10497
Eric Andersencb57d552001-06-28 07:25:16 +000010498 n1 = pipeline();
10499 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010500 t = readtoken();
10501 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000010502 t = NAND;
10503 } else if (t == TOR) {
10504 t = NOR;
10505 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010506 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010507 return n1;
10508 }
Eric Andersenc470f442003-07-28 09:56:35 +000010509 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010510 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010511 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000010512 n3->type = t;
10513 n3->nbinary.ch1 = n1;
10514 n3->nbinary.ch2 = n2;
10515 n1 = n3;
10516 }
10517}
10518
Eric Andersenc470f442003-07-28 09:56:35 +000010519static union node *
10520pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010521{
Eric Andersencb57d552001-06-28 07:25:16 +000010522 union node *n1, *n2, *pipenode;
10523 struct nodelist *lp, *prev;
10524 int negate;
10525
10526 negate = 0;
10527 TRACE(("pipeline: entered\n"));
10528 if (readtoken() == TNOT) {
10529 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000010530 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010531 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010532 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010533 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010534 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010535 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000010536 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010537 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010538 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000010539 pipenode->npipe.cmdlist = lp;
10540 lp->n = n1;
10541 do {
10542 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010543 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000010544 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010545 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000010546 prev->next = lp;
10547 } while (readtoken() == TPIPE);
10548 lp->next = NULL;
10549 n1 = pipenode;
10550 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010551 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010552 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010553 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000010554 n2->type = NNOT;
10555 n2->nnot.com = n1;
10556 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010557 }
10558 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000010559}
10560
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010561static union node *
10562makename(void)
10563{
10564 union node *n;
10565
Denis Vlasenko597906c2008-02-20 16:38:54 +000010566 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010567 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010568 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010569 n->narg.text = wordtext;
10570 n->narg.backquote = backquotelist;
10571 return n;
10572}
10573
10574static void
10575fixredir(union node *n, const char *text, int err)
10576{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010577 int fd;
10578
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010579 TRACE(("Fix redir %s %d\n", text, err));
10580 if (!err)
10581 n->ndup.vname = NULL;
10582
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000010583 fd = bb_strtou(text, NULL, 10);
10584 if (!errno && fd >= 0)
10585 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010586 else if (LONE_DASH(text))
10587 n->ndup.dupfd = -1;
10588 else {
10589 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010590 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010591 n->ndup.vname = makename();
10592 }
10593}
10594
10595/*
10596 * Returns true if the text contains nothing to expand (no dollar signs
10597 * or backquotes).
10598 */
10599static int
Denis Vlasenko68819d12008-12-15 11:26:36 +000010600noexpand(const char *text)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010601{
Denys Vlasenkocd716832009-11-28 22:14:02 +010010602 unsigned char c;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010603
Denys Vlasenkocd716832009-11-28 22:14:02 +010010604 while ((c = *text++) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010605 if (c == CTLQUOTEMARK)
10606 continue;
10607 if (c == CTLESC)
Denys Vlasenkocd716832009-11-28 22:14:02 +010010608 text++;
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010010609 else if (SIT(c, BASESYNTAX) == CCTL)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010610 return 0;
10611 }
10612 return 1;
10613}
10614
10615static void
10616parsefname(void)
10617{
10618 union node *n = redirnode;
10619
10620 if (readtoken() != TWORD)
10621 raise_error_unexpected_syntax(-1);
10622 if (n->type == NHERE) {
10623 struct heredoc *here = heredoc;
10624 struct heredoc *p;
10625 int i;
10626
10627 if (quoteflag == 0)
10628 n->type = NXHERE;
10629 TRACE(("Here document %d\n", n->type));
10630 if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
Denis Vlasenko559691a2008-10-05 18:39:31 +000010631 raise_error_syntax("illegal eof marker for << redirection");
Denys Vlasenkob6c84342009-08-29 20:23:20 +020010632 rmescapes(wordtext, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010633 here->eofmark = wordtext;
10634 here->next = NULL;
10635 if (heredoclist == NULL)
10636 heredoclist = here;
10637 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010638 for (p = heredoclist; p->next; p = p->next)
10639 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010640 p->next = here;
10641 }
10642 } else if (n->type == NTOFD || n->type == NFROMFD) {
10643 fixredir(n, wordtext, 0);
10644 } else {
10645 n->nfile.fname = makename();
10646 }
10647}
Eric Andersencb57d552001-06-28 07:25:16 +000010648
Eric Andersenc470f442003-07-28 09:56:35 +000010649static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010650simplecmd(void)
10651{
10652 union node *args, **app;
10653 union node *n = NULL;
10654 union node *vars, **vpp;
10655 union node **rpp, *redir;
10656 int savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010657#if ENABLE_ASH_BASH_COMPAT
10658 smallint double_brackets_flag = 0;
10659#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010660
10661 args = NULL;
10662 app = &args;
10663 vars = NULL;
10664 vpp = &vars;
10665 redir = NULL;
10666 rpp = &redir;
10667
10668 savecheckkwd = CHKALIAS;
10669 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000010670 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010671 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010672 t = readtoken();
10673 switch (t) {
10674#if ENABLE_ASH_BASH_COMPAT
10675 case TAND: /* "&&" */
10676 case TOR: /* "||" */
10677 if (!double_brackets_flag) {
10678 tokpushback = 1;
10679 goto out;
10680 }
10681 wordtext = (char *) (t == TAND ? "-a" : "-o");
10682#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010683 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010684 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010685 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010686 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010687 n->narg.text = wordtext;
Denis Vlasenko80591b02008-03-25 07:49:43 +000010688#if ENABLE_ASH_BASH_COMPAT
10689 if (strcmp("[[", wordtext) == 0)
10690 double_brackets_flag = 1;
10691 else if (strcmp("]]", wordtext) == 0)
10692 double_brackets_flag = 0;
10693#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010694 n->narg.backquote = backquotelist;
10695 if (savecheckkwd && isassignment(wordtext)) {
10696 *vpp = n;
10697 vpp = &n->narg.next;
10698 } else {
10699 *app = n;
10700 app = &n->narg.next;
10701 savecheckkwd = 0;
10702 }
10703 break;
10704 case TREDIR:
10705 *rpp = n = redirnode;
10706 rpp = &n->nfile.next;
10707 parsefname(); /* read name of redirection file */
10708 break;
10709 case TLP:
10710 if (args && app == &args->narg.next
10711 && !vars && !redir
10712 ) {
10713 struct builtincmd *bcmd;
10714 const char *name;
10715
10716 /* We have a function */
10717 if (readtoken() != TRP)
10718 raise_error_unexpected_syntax(TRP);
10719 name = n->narg.text;
10720 if (!goodname(name)
10721 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
10722 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000010723 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010724 }
10725 n->type = NDEFUN;
10726 checkkwd = CHKNL | CHKKWD | CHKALIAS;
10727 n->narg.next = parse_command();
10728 return n;
10729 }
10730 /* fall through */
10731 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010732 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010733 goto out;
10734 }
10735 }
10736 out:
10737 *app = NULL;
10738 *vpp = NULL;
10739 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010740 n = stzalloc(sizeof(struct ncmd));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010741 n->type = NCMD;
10742 n->ncmd.args = args;
10743 n->ncmd.assign = vars;
10744 n->ncmd.redirect = redir;
10745 return n;
10746}
10747
10748static union node *
10749parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010750{
Eric Andersencb57d552001-06-28 07:25:16 +000010751 union node *n1, *n2;
10752 union node *ap, **app;
10753 union node *cp, **cpp;
10754 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000010755 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010756 int t;
10757
10758 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010759 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000010760
Eric Andersencb57d552001-06-28 07:25:16 +000010761 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000010762 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010763 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000010764 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000010765 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010766 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010767 n1->type = NIF;
10768 n1->nif.test = list(0);
10769 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010770 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010771 n1->nif.ifpart = list(0);
10772 n2 = n1;
10773 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010774 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000010775 n2 = n2->nif.elsepart;
10776 n2->type = NIF;
10777 n2->nif.test = list(0);
10778 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010779 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000010780 n2->nif.ifpart = list(0);
10781 }
10782 if (lasttoken == TELSE)
10783 n2->nif.elsepart = list(0);
10784 else {
10785 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010786 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010787 }
Eric Andersenc470f442003-07-28 09:56:35 +000010788 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000010789 break;
10790 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000010791 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000010792 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010793 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010794 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000010795 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010796 got = readtoken();
10797 if (got != TDO) {
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020010798 TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
Denis Vlasenko131ae172007-02-18 13:00:19 +000010799 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010800 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010801 }
10802 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010803 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010804 break;
10805 }
10806 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000010807 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000010808 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010809 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000010810 n1->type = NFOR;
10811 n1->nfor.var = wordtext;
Eric Andersenc470f442003-07-28 09:56:35 +000010812 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010813 if (readtoken() == TIN) {
10814 app = &ap;
10815 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010816 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010817 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010818 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010819 n2->narg.text = wordtext;
10820 n2->narg.backquote = backquotelist;
10821 *app = n2;
10822 app = &n2->narg.next;
10823 }
10824 *app = NULL;
10825 n1->nfor.args = ap;
10826 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010827 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000010828 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010829 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010830 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010831 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010832 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010833 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000010834 n1->nfor.args = n2;
10835 /*
10836 * Newline or semicolon here is optional (but note
10837 * that the original Bourne shell only allowed NL).
10838 */
10839 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010840 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010841 }
Eric Andersenc470f442003-07-28 09:56:35 +000010842 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010843 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010844 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000010845 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010846 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000010847 break;
10848 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010849 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000010850 n1->type = NCASE;
10851 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010852 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010853 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010854 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010855 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010856 n2->narg.text = wordtext;
10857 n2->narg.backquote = backquotelist;
Eric Andersencb57d552001-06-28 07:25:16 +000010858 do {
Eric Andersenc470f442003-07-28 09:56:35 +000010859 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000010860 } while (readtoken() == TNL);
10861 if (lasttoken != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010862 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000010863 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010864 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000010865 checkkwd = CHKNL | CHKKWD;
10866 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010867 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010868 if (lasttoken == TLP)
10869 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000010870 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000010871 cp->type = NCLIST;
10872 app = &cp->nclist.pattern;
10873 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010874 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000010875 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010876 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000010877 ap->narg.text = wordtext;
10878 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000010879 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000010880 break;
10881 app = &ap->narg.next;
10882 readtoken();
10883 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000010884 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000010885 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010886 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000010887 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000010888
Eric Andersenc470f442003-07-28 09:56:35 +000010889 cpp = &cp->nclist.next;
10890
10891 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010892 t = readtoken();
10893 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000010894 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010895 raise_error_unexpected_syntax(TENDCASE);
10896 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000010897 }
Eric Andersenc470f442003-07-28 09:56:35 +000010898 }
Eric Andersencb57d552001-06-28 07:25:16 +000010899 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000010900 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000010901 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000010902 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010903 n1->type = NSUBSHELL;
10904 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000010905 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000010906 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000010907 break;
10908 case TBEGIN:
10909 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000010910 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000010911 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010912 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000010913 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010914 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000010915 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000010916 }
10917
Eric Andersenc470f442003-07-28 09:56:35 +000010918 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000010919 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000010920
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010921 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000010922 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000010923 checkkwd = CHKKWD | CHKALIAS;
10924 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000010925 while (readtoken() == TREDIR) {
10926 *rpp = n2 = redirnode;
10927 rpp = &n2->nfile.next;
10928 parsefname();
10929 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000010930 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010931 *rpp = NULL;
10932 if (redir) {
10933 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000010934 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000010935 n2->type = NREDIR;
10936 n2->nredir.n = n1;
10937 n1 = n2;
10938 }
10939 n1->nredir.redirect = redir;
10940 }
Eric Andersencb57d552001-06-28 07:25:16 +000010941 return n1;
10942}
10943
Denis Vlasenkoef527f52008-06-23 01:52:30 +000010944#if ENABLE_ASH_BASH_COMPAT
10945static int decode_dollar_squote(void)
10946{
10947 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
10948 int c, cnt;
10949 char *p;
10950 char buf[4];
10951
10952 c = pgetc();
10953 p = strchr(C_escapes, c);
10954 if (p) {
10955 buf[0] = c;
10956 p = buf;
10957 cnt = 3;
10958 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
10959 do {
10960 c = pgetc();
10961 *++p = c;
10962 } while ((unsigned char)(c - '0') <= 7 && --cnt);
10963 pungetc();
10964 } else if (c == 'x') { /* \xHH */
10965 do {
10966 c = pgetc();
10967 *++p = c;
10968 } while (isxdigit(c) && --cnt);
10969 pungetc();
10970 if (cnt == 3) { /* \x but next char is "bad" */
10971 c = 'x';
10972 goto unrecognized;
10973 }
10974 } else { /* simple seq like \\ or \t */
10975 p++;
10976 }
10977 *p = '\0';
10978 p = buf;
10979 c = bb_process_escape_sequence((void*)&p);
10980 } else { /* unrecognized "\z": print both chars unless ' or " */
10981 if (c != '\'' && c != '"') {
10982 unrecognized:
10983 c |= 0x100; /* "please encode \, then me" */
10984 }
10985 }
10986 return c;
10987}
10988#endif
10989
Eric Andersencb57d552001-06-28 07:25:16 +000010990/*
10991 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10992 * is not NULL, read a here document. In the latter case, eofmark is the
10993 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010010994 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000010995 * is the first character of the input token or document.
10996 *
10997 * Because C does not have internal subroutines, I have simulated them
10998 * using goto's to implement the subroutine linkage. The following macros
10999 * will run code that appears at the end of readtoken1.
11000 */
Eric Andersen2870d962001-07-02 17:27:21 +000011001#define CHECKEND() {goto checkend; checkend_return:;}
11002#define PARSEREDIR() {goto parseredir; parseredir_return:;}
11003#define PARSESUB() {goto parsesub; parsesub_return:;}
11004#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
11005#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
11006#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000011007static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010011008readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000011009{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011010 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010011011 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000011012 char *out;
11013 int len;
11014 char line[EOFMARKLEN + 1];
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011015 struct nodelist *bqlist;
11016 smallint quotef;
11017 smallint dblquote;
11018 smallint oldstyle;
11019 smallint prevsyntax; /* syntax before arithmetic */
Denis Vlasenko46a53062007-09-24 18:30:02 +000011020#if ENABLE_ASH_EXPAND_PRMT
11021 smallint pssyntax; /* we are expanding a prompt string */
11022#endif
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011023 int varnest; /* levels of variables expansion */
11024 int arinest; /* levels of arithmetic expansion */
11025 int parenlevel; /* levels of parens in arithmetic */
11026 int dqvarnest; /* levels of variables expansion within double quotes */
11027
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011028 IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011029
Eric Andersencb57d552001-06-28 07:25:16 +000011030#if __GNUC__
11031 /* Avoid longjmp clobbering */
11032 (void) &out;
11033 (void) &quotef;
11034 (void) &dblquote;
11035 (void) &varnest;
11036 (void) &arinest;
11037 (void) &parenlevel;
11038 (void) &dqvarnest;
11039 (void) &oldstyle;
11040 (void) &prevsyntax;
11041 (void) &syntax;
11042#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011043 startlinno = g_parsefile->linno;
Eric Andersencb57d552001-06-28 07:25:16 +000011044 bqlist = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011045 quotef = 0;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011046 prevsyntax = 0;
Denis Vlasenko46a53062007-09-24 18:30:02 +000011047#if ENABLE_ASH_EXPAND_PRMT
11048 pssyntax = (syntax == PSSYNTAX);
11049 if (pssyntax)
11050 syntax = DQSYNTAX;
11051#endif
11052 dblquote = (syntax == DQSYNTAX);
Eric Andersencb57d552001-06-28 07:25:16 +000011053 varnest = 0;
11054 arinest = 0;
11055 parenlevel = 0;
11056 dqvarnest = 0;
11057
11058 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011059 loop:
11060 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020011061 CHECKEND(); /* set c to PEOF if at end of here document */
11062 for (;;) { /* until end of line or end of word */
11063 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
11064 switch (SIT(c, syntax)) {
11065 case CNL: /* '\n' */
11066 if (syntax == BASESYNTAX)
11067 goto endword; /* exit outer loop */
11068 USTPUTC(c, out);
11069 g_parsefile->linno++;
11070 setprompt_if(doprompt, 2);
11071 c = pgetc();
11072 goto loop; /* continue outer loop */
11073 case CWORD:
11074 USTPUTC(c, out);
11075 break;
11076 case CCTL:
11077 if (eofmark == NULL || dblquote)
11078 USTPUTC(CTLESC, out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011079#if ENABLE_ASH_BASH_COMPAT
Denys Vlasenko958581a2010-09-12 15:04:27 +020011080 if (c == '\\' && bash_dollar_squote) {
11081 c = decode_dollar_squote();
11082 if (c & 0x100) {
11083 USTPUTC('\\', out);
11084 c = (unsigned char)c;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011085 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011086 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011087#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011088 USTPUTC(c, out);
11089 break;
11090 case CBACK: /* backslash */
11091 c = pgetc_without_PEOA();
11092 if (c == PEOF) {
11093 USTPUTC(CTLESC, out);
11094 USTPUTC('\\', out);
11095 pungetc();
11096 } else if (c == '\n') {
11097 setprompt_if(doprompt, 2);
11098 } else {
11099#if ENABLE_ASH_EXPAND_PRMT
11100 if (c == '$' && pssyntax) {
Eric Andersenc470f442003-07-28 09:56:35 +000011101 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011102 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011103 }
Denis Vlasenko46a53062007-09-24 18:30:02 +000011104#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020011105 /* Backslash is retained if we are in "str" and next char isn't special */
11106 if (dblquote
11107 && c != '\\'
11108 && c != '`'
11109 && c != '$'
11110 && (c != '"' || eofmark != NULL)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011111 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011112 USTPUTC(CTLESC, out);
11113 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011114 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011115 if (SIT(c, SQSYNTAX) == CCTL)
11116 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020011117 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020011118 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011119 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011120 break;
11121 case CSQUOTE:
11122 syntax = SQSYNTAX;
11123 quotemark:
11124 if (eofmark == NULL) {
11125 USTPUTC(CTLQUOTEMARK, out);
11126 }
11127 break;
11128 case CDQUOTE:
11129 syntax = DQSYNTAX;
11130 dblquote = 1;
11131 goto quotemark;
11132 case CENDQUOTE:
11133 IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
11134 if (eofmark != NULL && arinest == 0
11135 && varnest == 0
11136 ) {
11137 USTPUTC(c, out);
11138 } else {
11139 if (dqvarnest == 0) {
11140 syntax = BASESYNTAX;
11141 dblquote = 0;
11142 }
11143 quotef = 1;
11144 goto quotemark;
11145 }
11146 break;
11147 case CVAR: /* '$' */
11148 PARSESUB(); /* parse substitution */
11149 break;
11150 case CENDVAR: /* '}' */
11151 if (varnest > 0) {
11152 varnest--;
11153 if (dqvarnest > 0) {
11154 dqvarnest--;
11155 }
11156 c = CTLENDVAR;
11157 }
11158 USTPUTC(c, out);
11159 break;
11160#if ENABLE_SH_MATH_SUPPORT
11161 case CLP: /* '(' in arithmetic */
11162 parenlevel++;
11163 USTPUTC(c, out);
11164 break;
11165 case CRP: /* ')' in arithmetic */
11166 if (parenlevel > 0) {
11167 parenlevel--;
11168 } else {
11169 if (pgetc() == ')') {
11170 if (--arinest == 0) {
11171 syntax = prevsyntax;
11172 dblquote = (syntax == DQSYNTAX);
11173 c = CTLENDARI;
11174 }
11175 } else {
11176 /*
11177 * unbalanced parens
11178 * (don't 2nd guess - no error)
11179 */
11180 pungetc();
11181 }
11182 }
11183 USTPUTC(c, out);
11184 break;
11185#endif
11186 case CBQUOTE: /* '`' */
11187 PARSEBACKQOLD();
11188 break;
11189 case CENDFILE:
11190 goto endword; /* exit outer loop */
11191 case CIGN:
11192 break;
11193 default:
11194 if (varnest == 0) {
11195#if ENABLE_ASH_BASH_COMPAT
11196 if (c == '&') {
11197 if (pgetc() == '>')
11198 c = 0x100 + '>'; /* flag &> */
11199 pungetc();
11200 }
11201#endif
11202 goto endword; /* exit outer loop */
11203 }
11204 IF_ASH_ALIAS(if (c != PEOA))
11205 USTPUTC(c, out);
11206 }
11207 c = pgetc_fast();
11208 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011209 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020011210
Mike Frysinger98c52642009-04-02 10:02:37 +000011211#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011212 if (syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011213 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000011214#endif
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011215 if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011216 raise_error_syntax("unterminated quoted string");
Eric Andersencb57d552001-06-28 07:25:16 +000011217 if (varnest != 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011218 startlinno = g_parsefile->linno;
Eric Andersenc470f442003-07-28 09:56:35 +000011219 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000011220 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000011221 }
11222 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011223 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011224 out = stackblock();
11225 if (eofmark == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011226 if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000011227 && quotef == 0
11228 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011229 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011230 PARSEREDIR(); /* passed as params: out, c */
11231 lasttoken = TREDIR;
11232 return lasttoken;
11233 }
11234 /* else: non-number X seen, interpret it
11235 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000011236 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011237 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011238 }
11239 quoteflag = quotef;
11240 backquotelist = bqlist;
11241 grabstackblock(len);
11242 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011243 lasttoken = TWORD;
11244 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011245/* end of readtoken routine */
11246
Eric Andersencb57d552001-06-28 07:25:16 +000011247/*
11248 * Check to see whether we are at the end of the here document. When this
11249 * is called, c is set to the first character of the next input line. If
11250 * we are at the end of the here document, this routine sets the c to PEOF.
11251 */
Eric Andersenc470f442003-07-28 09:56:35 +000011252checkend: {
11253 if (eofmark) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000011254#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011255 if (c == PEOA)
11256 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000011257#endif
11258 if (striptabs) {
11259 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011260 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000011261 }
Eric Andersenc470f442003-07-28 09:56:35 +000011262 }
11263 if (c == *eofmark) {
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011264 if (pfgets(line, sizeof(line)) != NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000011265 char *p, *q;
Eric Andersencb57d552001-06-28 07:25:16 +000011266
Eric Andersenc470f442003-07-28 09:56:35 +000011267 p = line;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011268 for (q = eofmark + 1; *q && *p == *q; p++, q++)
11269 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000011270 if (*p == '\n' && *q == '\0') {
11271 c = PEOF;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011272 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011273 needprompt = doprompt;
11274 } else {
11275 pushstring(line, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000011276 }
11277 }
11278 }
11279 }
Eric Andersenc470f442003-07-28 09:56:35 +000011280 goto checkend_return;
11281}
Eric Andersencb57d552001-06-28 07:25:16 +000011282
Eric Andersencb57d552001-06-28 07:25:16 +000011283/*
11284 * Parse a redirection operator. The variable "out" points to a string
11285 * specifying the fd to be redirected. The variable "c" contains the
11286 * first character of the redirection operator.
11287 */
Eric Andersenc470f442003-07-28 09:56:35 +000011288parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011289 /* out is already checked to be a valid number or "" */
11290 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000011291 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000011292
Denis Vlasenko597906c2008-02-20 16:38:54 +000011293 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000011294 if (c == '>') {
11295 np->nfile.fd = 1;
11296 c = pgetc();
11297 if (c == '>')
11298 np->type = NAPPEND;
11299 else if (c == '|')
11300 np->type = NCLOBBER;
11301 else if (c == '&')
11302 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000011303 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000011304 else {
11305 np->type = NTO;
11306 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000011307 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000011308 }
11309#if ENABLE_ASH_BASH_COMPAT
11310 else if (c == 0x100 + '>') { /* this flags &> redirection */
11311 np->nfile.fd = 1;
11312 pgetc(); /* this is '>', no need to check */
11313 np->type = NTO2;
11314 }
11315#endif
11316 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000011317 /*np->nfile.fd = 0; - stzalloc did it */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011318 c = pgetc();
11319 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000011320 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011321 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011322 np = stzalloc(sizeof(struct nhere));
11323 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011324 }
11325 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011326 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000011327 heredoc->here = np;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011328 c = pgetc();
11329 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000011330 heredoc->striptabs = 1;
11331 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011332 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011333 pungetc();
11334 }
11335 break;
11336
11337 case '&':
11338 np->type = NFROMFD;
11339 break;
11340
11341 case '>':
11342 np->type = NFROMTO;
11343 break;
11344
11345 default:
11346 np->type = NFROM;
11347 pungetc();
11348 break;
11349 }
Eric Andersencb57d552001-06-28 07:25:16 +000011350 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011351 if (fd >= 0)
11352 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011353 redirnode = np;
11354 goto parseredir_return;
11355}
Eric Andersencb57d552001-06-28 07:25:16 +000011356
Eric Andersencb57d552001-06-28 07:25:16 +000011357/*
11358 * Parse a substitution. At this point, we have read the dollar sign
11359 * and nothing else.
11360 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000011361
11362/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
11363 * (assuming ascii char codes, as the original implementation did) */
11364#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011365 (((unsigned)(c) - 33 < 32) \
11366 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000011367parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010011368 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000011369 int typeloc;
11370 int flags;
Eric Andersencb57d552001-06-28 07:25:16 +000011371
Eric Andersenc470f442003-07-28 09:56:35 +000011372 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011373 if (c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011374 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000011375 ) {
Denis Vlasenkoef527f52008-06-23 01:52:30 +000011376#if ENABLE_ASH_BASH_COMPAT
11377 if (c == '\'')
11378 bash_dollar_squote = 1;
11379 else
11380#endif
11381 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011382 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011383 } else if (c == '(') {
11384 /* $(command) or $((arith)) */
Eric Andersenc470f442003-07-28 09:56:35 +000011385 if (pgetc() == '(') {
Mike Frysinger98c52642009-04-02 10:02:37 +000011386#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000011387 PARSEARITH();
11388#else
Mike Frysinger98a6f562008-06-09 09:38:45 +000011389 raise_error_syntax("you disabled math support for $((arith)) syntax");
Eric Andersenc470f442003-07-28 09:56:35 +000011390#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011391 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011392 pungetc();
11393 PARSEBACKQNEW();
11394 }
11395 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011396 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Eric Andersenc470f442003-07-28 09:56:35 +000011397 USTPUTC(CTLVAR, out);
11398 typeloc = out - (char *)stackblock();
11399 USTPUTC(VSNORMAL, out);
11400 subtype = VSNORMAL;
11401 if (c == '{') {
11402 c = pgetc();
11403 if (c == '#') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011404 c = pgetc();
11405 if (c == '}')
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011406 c = '#'; /* ${#} - same as $# */
Eric Andersenc470f442003-07-28 09:56:35 +000011407 else
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011408 subtype = VSLENGTH; /* ${#VAR} */
11409 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011410 subtype = 0;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011411 }
Eric Andersenc470f442003-07-28 09:56:35 +000011412 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011413 if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011414 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011415 do {
11416 STPUTC(c, out);
Eric Andersencb57d552001-06-28 07:25:16 +000011417 c = pgetc();
Denys Vlasenkocd716832009-11-28 22:14:02 +010011418 } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011419 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011420 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011421 do {
11422 STPUTC(c, out);
11423 c = pgetc();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011424 } while (isdigit(c));
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011425 } else if (is_special(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011426 /* $[{[#]]<specialchar>[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000011427 USTPUTC(c, out);
11428 c = pgetc();
Denis Vlasenko559691a2008-10-05 18:39:31 +000011429 } else {
11430 badsub:
11431 raise_error_syntax("bad substitution");
11432 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011433 if (c != '}' && subtype == VSLENGTH) {
11434 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020011435 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011436 }
Eric Andersencb57d552001-06-28 07:25:16 +000011437
Eric Andersenc470f442003-07-28 09:56:35 +000011438 STPUTC('=', out);
11439 flags = 0;
11440 if (subtype == 0) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011441 /* ${VAR...} but not $VAR or ${#VAR} */
11442 /* c == first char after VAR */
Eric Andersenc470f442003-07-28 09:56:35 +000011443 switch (c) {
11444 case ':':
Eric Andersenc470f442003-07-28 09:56:35 +000011445 c = pgetc();
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011446#if ENABLE_ASH_BASH_COMPAT
11447 if (c == ':' || c == '$' || isdigit(c)) {
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011448//TODO: support more general format ${v:EXPR:EXPR},
11449// where EXPR follows $(()) rules
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011450 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011451 pungetc();
11452 break; /* "goto do_pungetc" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011453 }
11454#endif
11455 flags = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000011456 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011457 default: {
11458 static const char types[] ALIGN1 = "}-+?=";
11459 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000011460 if (p == NULL)
11461 goto badsub;
11462 subtype = p - types + VSNORMAL;
11463 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011464 }
Eric Andersenc470f442003-07-28 09:56:35 +000011465 case '%':
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011466 case '#': {
11467 int cc = c;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011468 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011469 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011470 if (c != cc)
11471 goto do_pungetc;
11472 subtype++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011473 break;
11474 }
11475#if ENABLE_ASH_BASH_COMPAT
11476 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020011477 /* ${v/[/]pattern/repl} */
11478//TODO: encode pattern and repl separately.
11479// Currently ${v/$var_with_slash/repl} is horribly broken
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011480 subtype = VSREPLACE;
11481 c = pgetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011482 if (c != '/')
11483 goto do_pungetc;
11484 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000011485 break;
11486#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011487 }
Eric Andersenc470f442003-07-28 09:56:35 +000011488 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020011489 do_pungetc:
Eric Andersenc470f442003-07-28 09:56:35 +000011490 pungetc();
11491 }
11492 if (dblquote || arinest)
11493 flags |= VSQUOTE;
Denys Vlasenkocd716832009-11-28 22:14:02 +010011494 ((unsigned char *)stackblock())[typeloc] = subtype | flags;
Eric Andersenc470f442003-07-28 09:56:35 +000011495 if (subtype != VSNORMAL) {
11496 varnest++;
11497 if (dblquote || arinest) {
11498 dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000011499 }
11500 }
11501 }
Eric Andersenc470f442003-07-28 09:56:35 +000011502 goto parsesub_return;
11503}
Eric Andersencb57d552001-06-28 07:25:16 +000011504
Eric Andersencb57d552001-06-28 07:25:16 +000011505/*
11506 * Called to parse command substitutions. Newstyle is set if the command
11507 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
11508 * list of commands (passed by reference), and savelen is the number of
11509 * characters on the top of the stack which must be preserved.
11510 */
Eric Andersenc470f442003-07-28 09:56:35 +000011511parsebackq: {
11512 struct nodelist **nlpp;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011513 smallint savepbq;
Eric Andersenc470f442003-07-28 09:56:35 +000011514 union node *n;
11515 char *volatile str;
11516 struct jmploc jmploc;
11517 struct jmploc *volatile savehandler;
11518 size_t savelen;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011519 smallint saveprompt = 0;
11520
Eric Andersencb57d552001-06-28 07:25:16 +000011521#ifdef __GNUC__
Eric Andersenc470f442003-07-28 09:56:35 +000011522 (void) &saveprompt;
Eric Andersencb57d552001-06-28 07:25:16 +000011523#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011524 savepbq = parsebackquote;
11525 if (setjmp(jmploc.loc)) {
Denis Vlasenko60818682007-09-28 22:07:23 +000011526 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011527 parsebackquote = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011528 exception_handler = savehandler;
11529 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000011530 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011531 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000011532 str = NULL;
11533 savelen = out - (char *)stackblock();
11534 if (savelen > 0) {
11535 str = ckmalloc(savelen);
11536 memcpy(str, stackblock(), savelen);
11537 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011538 savehandler = exception_handler;
11539 exception_handler = &jmploc;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011540 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011541 if (oldstyle) {
11542 /* We must read until the closing backquote, giving special
11543 treatment to some slashes, and then push the string and
11544 reread it as input, interpreting it normally. */
11545 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000011546 size_t psavelen;
11547 char *pstr;
11548
Eric Andersenc470f442003-07-28 09:56:35 +000011549 STARTSTACKSTR(pout);
11550 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011551 int pc;
11552
11553 setprompt_if(needprompt, 2);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011554 pc = pgetc();
11555 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000011556 case '`':
11557 goto done;
11558
11559 case '\\':
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011560 pc = pgetc();
11561 if (pc == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011562 g_parsefile->linno++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011563 setprompt_if(doprompt, 2);
Eric Andersenc470f442003-07-28 09:56:35 +000011564 /*
11565 * If eating a newline, avoid putting
11566 * the newline into the new character
11567 * stream (via the STPUTC after the
11568 * switch).
11569 */
11570 continue;
11571 }
11572 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011573 && (!dblquote || pc != '"')
11574 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000011575 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010011576 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010011577 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000011578 break;
11579 }
11580 /* fall through */
11581
11582 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011583 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011584 startlinno = g_parsefile->linno;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011585 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000011586
11587 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011588 g_parsefile->linno++;
Eric Andersenc470f442003-07-28 09:56:35 +000011589 needprompt = doprompt;
11590 break;
11591
11592 default:
11593 break;
11594 }
11595 STPUTC(pc, pout);
11596 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011597 done:
Eric Andersenc470f442003-07-28 09:56:35 +000011598 STPUTC('\0', pout);
11599 psavelen = pout - (char *)stackblock();
11600 if (psavelen > 0) {
11601 pstr = grabstackstr(pout);
11602 setinputstring(pstr);
11603 }
11604 }
11605 nlpp = &bqlist;
11606 while (*nlpp)
11607 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011608 *nlpp = stzalloc(sizeof(**nlpp));
11609 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011610 parsebackquote = oldstyle;
11611
11612 if (oldstyle) {
11613 saveprompt = doprompt;
11614 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011615 }
11616
Eric Andersenc470f442003-07-28 09:56:35 +000011617 n = list(2);
11618
11619 if (oldstyle)
11620 doprompt = saveprompt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011621 else if (readtoken() != TRP)
11622 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000011623
11624 (*nlpp)->n = n;
11625 if (oldstyle) {
11626 /*
11627 * Start reading from old file again, ignoring any pushed back
11628 * tokens left from the backquote parsing
11629 */
11630 popfile();
11631 tokpushback = 0;
11632 }
11633 while (stackblocksize() <= savelen)
11634 growstackblock();
11635 STARTSTACKSTR(out);
11636 if (str) {
11637 memcpy(out, str, savelen);
11638 STADJUST(savelen, out);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011639 INT_OFF;
11640 free(str);
Eric Andersenc470f442003-07-28 09:56:35 +000011641 str = NULL;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011642 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011643 }
11644 parsebackquote = savepbq;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011645 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000011646 if (arinest || dblquote)
11647 USTPUTC(CTLBACKQ | CTLQUOTE, out);
11648 else
11649 USTPUTC(CTLBACKQ, out);
11650 if (oldstyle)
11651 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011652 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000011653}
11654
Mike Frysinger98c52642009-04-02 10:02:37 +000011655#if ENABLE_SH_MATH_SUPPORT
Eric Andersencb57d552001-06-28 07:25:16 +000011656/*
11657 * Parse an arithmetic expansion (indicate start of one and set state)
11658 */
Eric Andersenc470f442003-07-28 09:56:35 +000011659parsearith: {
Eric Andersenc470f442003-07-28 09:56:35 +000011660 if (++arinest == 1) {
11661 prevsyntax = syntax;
11662 syntax = ARISYNTAX;
11663 USTPUTC(CTLARI, out);
11664 if (dblquote)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011665 USTPUTC('"', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011666 else
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011667 USTPUTC(' ', out);
Eric Andersenc470f442003-07-28 09:56:35 +000011668 } else {
11669 /*
11670 * we collapse embedded arithmetic expansion to
11671 * parenthesis, which should be equivalent
11672 */
11673 USTPUTC('(', out);
Eric Andersencb57d552001-06-28 07:25:16 +000011674 }
Eric Andersenc470f442003-07-28 09:56:35 +000011675 goto parsearith_return;
11676}
11677#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011678
Eric Andersenc470f442003-07-28 09:56:35 +000011679} /* end of readtoken */
11680
Eric Andersencb57d552001-06-28 07:25:16 +000011681/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011682 * Read the next input token.
11683 * If the token is a word, we set backquotelist to the list of cmds in
11684 * backquotes. We set quoteflag to true if any part of the word was
11685 * quoted.
11686 * If the token is TREDIR, then we set redirnode to a structure containing
11687 * the redirection.
11688 * In all cases, the variable startlinno is set to the number of the line
11689 * on which the token starts.
11690 *
11691 * [Change comment: here documents and internal procedures]
11692 * [Readtoken shouldn't have any arguments. Perhaps we should make the
11693 * word parsing code into a separate routine. In this case, readtoken
11694 * doesn't need to have any internal procedures, but parseword does.
11695 * We could also make parseoperator in essence the main routine, and
11696 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000011697 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011698#define NEW_xxreadtoken
11699#ifdef NEW_xxreadtoken
11700/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011701static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000011702 '\n', '(', ')', /* singles */
11703 '&', '|', ';', /* doubles */
11704 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011705};
Eric Andersencb57d552001-06-28 07:25:16 +000011706
Denis Vlasenko834dee72008-10-07 09:18:30 +000011707#define xxreadtoken_singles 3
11708#define xxreadtoken_doubles 3
11709
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011710static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011711 TNL, TLP, TRP, /* only single occurrence allowed */
11712 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
11713 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000011714 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011715};
11716
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011717static int
11718xxreadtoken(void)
11719{
11720 int c;
11721
11722 if (tokpushback) {
11723 tokpushback = 0;
11724 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000011725 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011726 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011727 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011728 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011729 c = pgetc_fast();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000011730 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011731 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011732
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011733 if (c == '#') {
11734 while ((c = pgetc()) != '\n' && c != PEOF)
11735 continue;
11736 pungetc();
11737 } else if (c == '\\') {
11738 if (pgetc() != '\n') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011739 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011740 break; /* return readtoken1(...) */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011741 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011742 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011743 setprompt_if(doprompt, 2);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011744 } else {
11745 const char *p;
11746
11747 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
11748 if (c != PEOF) {
11749 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011750 g_parsefile->linno++;
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011751 needprompt = doprompt;
11752 }
11753
11754 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000011755 if (p == NULL)
11756 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011757
Denis Vlasenko834dee72008-10-07 09:18:30 +000011758 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
11759 int cc = pgetc();
11760 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011761 p += xxreadtoken_doubles + 1;
11762 } else {
11763 pungetc();
Denis Vlasenko834dee72008-10-07 09:18:30 +000011764#if ENABLE_ASH_BASH_COMPAT
11765 if (c == '&' && cc == '>') /* &> */
11766 break; /* return readtoken1(...) */
11767#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011768 }
11769 }
11770 }
11771 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
11772 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011773 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011774 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011775
11776 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011777}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011778#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011779#define RETURN(token) return lasttoken = token
11780static int
11781xxreadtoken(void)
11782{
11783 int c;
11784
11785 if (tokpushback) {
11786 tokpushback = 0;
11787 return lasttoken;
11788 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020011789 setprompt_if(needprompt, 2);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011790 startlinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011791 for (;;) { /* until token or start of word found */
Denis Vlasenko834dee72008-10-07 09:18:30 +000011792 c = pgetc_fast();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011793 switch (c) {
11794 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011795 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011796 continue;
11797 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011798 while ((c = pgetc()) != '\n' && c != PEOF)
11799 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011800 pungetc();
11801 continue;
11802 case '\\':
11803 if (pgetc() == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011804 startlinno = ++g_parsefile->linno;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011805 setprompt_if(doprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011806 continue;
11807 }
11808 pungetc();
11809 goto breakloop;
11810 case '\n':
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011811 g_parsefile->linno++;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011812 needprompt = doprompt;
11813 RETURN(TNL);
11814 case PEOF:
11815 RETURN(TEOF);
11816 case '&':
11817 if (pgetc() == '&')
11818 RETURN(TAND);
11819 pungetc();
11820 RETURN(TBACKGND);
11821 case '|':
11822 if (pgetc() == '|')
11823 RETURN(TOR);
11824 pungetc();
11825 RETURN(TPIPE);
11826 case ';':
11827 if (pgetc() == ';')
11828 RETURN(TENDCASE);
11829 pungetc();
11830 RETURN(TSEMI);
11831 case '(':
11832 RETURN(TLP);
11833 case ')':
11834 RETURN(TRP);
11835 default:
11836 goto breakloop;
11837 }
11838 }
11839 breakloop:
11840 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
11841#undef RETURN
11842}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000011843#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011844
11845static int
11846readtoken(void)
11847{
11848 int t;
11849#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011850 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011851#endif
11852
11853#if ENABLE_ASH_ALIAS
11854 top:
11855#endif
11856
11857 t = xxreadtoken();
11858
11859 /*
11860 * eat newlines
11861 */
11862 if (checkkwd & CHKNL) {
11863 while (t == TNL) {
11864 parseheredoc();
11865 t = xxreadtoken();
11866 }
11867 }
11868
11869 if (t != TWORD || quoteflag) {
11870 goto out;
11871 }
11872
11873 /*
11874 * check for keywords
11875 */
11876 if (checkkwd & CHKKWD) {
11877 const char *const *pp;
11878
11879 pp = findkwd(wordtext);
11880 if (pp) {
11881 lasttoken = t = pp - tokname_array;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011882 TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011883 goto out;
11884 }
11885 }
11886
11887 if (checkkwd & CHKALIAS) {
11888#if ENABLE_ASH_ALIAS
11889 struct alias *ap;
11890 ap = lookupalias(wordtext, 1);
11891 if (ap != NULL) {
11892 if (*ap->val) {
11893 pushstring(ap->val, ap);
11894 }
11895 goto top;
11896 }
11897#endif
11898 }
11899 out:
11900 checkkwd = 0;
11901#if DEBUG
11902 if (!alreadyseen)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011903 TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011904 else
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011905 TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011906#endif
11907 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000011908}
11909
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011910static char
11911peektoken(void)
11912{
11913 int t;
11914
11915 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011916 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011917 return tokname_array[t][0];
11918}
Eric Andersencb57d552001-06-28 07:25:16 +000011919
11920/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011921 * Read and parse a command. Returns NODE_EOF on end of file.
11922 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000011923 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011924static union node *
11925parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000011926{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011927 int t;
Eric Andersencb57d552001-06-28 07:25:16 +000011928
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011929 tokpushback = 0;
11930 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020011931 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011932 needprompt = 0;
11933 t = readtoken();
11934 if (t == TEOF)
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020011935 return NODE_EOF;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011936 if (t == TNL)
11937 return NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011938 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011939 return list(1);
11940}
11941
11942/*
11943 * Input any here documents.
11944 */
11945static void
11946parseheredoc(void)
11947{
11948 struct heredoc *here;
11949 union node *n;
11950
11951 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011952 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011953
11954 while (here) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020011955 setprompt_if(needprompt, 2);
11956 readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011957 here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000011958 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011959 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011960 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011961 n->narg.text = wordtext;
11962 n->narg.backquote = backquotelist;
11963 here->here->nhere.doc = n;
11964 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000011965 }
Eric Andersencb57d552001-06-28 07:25:16 +000011966}
11967
11968
11969/*
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000011970 * called by editline -- any expansions to the prompt should be added here.
Eric Andersencb57d552001-06-28 07:25:16 +000011971 */
Denis Vlasenko131ae172007-02-18 13:00:19 +000011972#if ENABLE_ASH_EXPAND_PRMT
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011973static const char *
11974expandstr(const char *ps)
11975{
11976 union node n;
11977
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000011978 /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
11979 * and token processing _can_ alter it (delete NULs etc). */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011980 setinputstring((char *)ps);
Denis Vlasenko46a53062007-09-24 18:30:02 +000011981 readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000011982 popfile();
11983
11984 n.narg.type = NARG;
11985 n.narg.next = NULL;
11986 n.narg.text = wordtext;
11987 n.narg.backquote = backquotelist;
11988
11989 expandarg(&n, NULL, 0);
11990 return stackblock();
11991}
11992#endif
11993
Denis Vlasenko0c032a42007-02-23 01:03:40 +000011994/*
11995 * Execute a command or commands contained in a string.
11996 */
11997static int
11998evalstring(char *s, int mask)
Eric Andersenc470f442003-07-28 09:56:35 +000011999{
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012000 union node *n;
12001 struct stackmark smark;
12002 int skip;
12003
12004 setinputstring(s);
12005 setstackmark(&smark);
12006
12007 skip = 0;
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012008 while ((n = parsecmd(0)) != NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012009 evaltree(n, 0);
12010 popstackmark(&smark);
12011 skip = evalskip;
12012 if (skip)
12013 break;
12014 }
12015 popfile();
12016
12017 skip &= mask;
12018 evalskip = skip;
12019 return skip;
Eric Andersenc470f442003-07-28 09:56:35 +000012020}
12021
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012022/*
12023 * The eval command.
12024 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012025static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012026evalcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012027{
12028 char *p;
12029 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012030
Denis Vlasenko68404f12008-03-17 09:00:54 +000012031 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012032 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012033 argv += 2;
12034 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012035 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012036 for (;;) {
12037 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012038 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012039 if (p == NULL)
12040 break;
12041 STPUTC(' ', concat);
12042 }
12043 STPUTC('\0', concat);
12044 p = grabstackstr(concat);
12045 }
12046 evalstring(p, ~SKIPEVAL);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012047 }
12048 return exitstatus;
12049}
12050
12051/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010012052 * Read and execute commands.
12053 * "Top" is nonzero for the top level command loop;
12054 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012055 */
12056static int
12057cmdloop(int top)
12058{
12059 union node *n;
12060 struct stackmark smark;
12061 int inter;
12062 int numeof = 0;
12063
12064 TRACE(("cmdloop(%d) called\n", top));
12065 for (;;) {
12066 int skip;
12067
12068 setstackmark(&smark);
12069#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000012070 if (doing_jobctl)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012071 showjobs(stderr, SHOW_CHANGED);
12072#endif
12073 inter = 0;
12074 if (iflag && top) {
12075 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012076 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012077 }
12078 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020012079#if DEBUG
12080 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020012081 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000012082#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020012083 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012084 if (!top || numeof >= 50)
12085 break;
12086 if (!stoppedjobs()) {
12087 if (!Iflag)
12088 break;
12089 out2str("\nUse \"exit\" to leave shell.\n");
12090 }
12091 numeof++;
12092 } else if (nflag == 0) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000012093 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
12094 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012095 numeof = 0;
12096 evaltree(n, 0);
12097 }
12098 popstackmark(&smark);
12099 skip = evalskip;
12100
12101 if (skip) {
12102 evalskip = 0;
12103 return skip & SKIPEVAL;
12104 }
12105 }
12106 return 0;
12107}
12108
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012109/*
12110 * Take commands from a file. To be compatible we should do a path
12111 * search for the file, which is necessary to find sub-commands.
12112 */
12113static char *
12114find_dot_file(char *name)
12115{
12116 char *fullname;
12117 const char *path = pathval();
12118 struct stat statb;
12119
12120 /* don't try this for absolute or relative paths */
12121 if (strchr(name, '/'))
12122 return name;
12123
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012124 /* IIRC standards do not say whether . is to be searched.
12125 * And it is even smaller this way, making it unconditional for now:
12126 */
12127 if (1) { /* ENABLE_ASH_BASH_COMPAT */
12128 fullname = name;
12129 goto try_cur_dir;
12130 }
12131
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012132 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenko8ad78e12009-02-15 12:40:30 +000012133 try_cur_dir:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012134 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
12135 /*
12136 * Don't bother freeing here, since it will
12137 * be freed by the caller.
12138 */
12139 return fullname;
12140 }
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012141 if (fullname != name)
12142 stunalloc(fullname);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000012143 }
12144
12145 /* not found in the PATH */
12146 ash_msg_and_raise_error("%s: not found", name);
12147 /* NOTREACHED */
12148}
12149
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012150static int FAST_FUNC
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012151dotcmd(int argc, char **argv)
12152{
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012153 char *fullname;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012154 struct strlist *sp;
12155 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012156
12157 for (sp = cmdenviron; sp; sp = sp->next)
Denis Vlasenko4222ae42007-02-25 02:37:49 +000012158 setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012159
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012160 if (!argv[1]) {
12161 /* bash says: "bash: .: filename argument required" */
12162 return 2; /* bash compat */
12163 }
12164
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012165 /* "false; . empty_file; echo $?" should print 0, not 1: */
12166 exitstatus = 0;
12167
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012168 fullname = find_dot_file(argv[1]);
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012169
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012170 argv += 2;
12171 argc -= 2;
12172 if (argc) { /* argc > 0, argv[0] != NULL */
12173 saveparam = shellparam;
12174 shellparam.malloced = 0;
12175 shellparam.nparam = argc;
12176 shellparam.p = argv;
12177 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012178
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012179 setinputfile(fullname, INPUT_PUSH_FILE);
12180 commandname = fullname;
12181 cmdloop(0);
12182 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012183
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020012184 if (argc) {
12185 freeparam(&shellparam);
12186 shellparam = saveparam;
12187 };
12188
Denys Vlasenkocd10dc42010-05-17 17:10:46 +020012189 return exitstatus;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012190}
12191
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012192static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012193exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012194{
12195 if (stoppedjobs())
12196 return 0;
Denis Vlasenko68404f12008-03-17 09:00:54 +000012197 if (argv[1])
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012198 exitstatus = number(argv[1]);
12199 raise_exception(EXEXIT);
12200 /* NOTREACHED */
12201}
12202
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012203/*
12204 * Read a file containing shell functions.
12205 */
12206static void
12207readcmdfile(char *name)
12208{
12209 setinputfile(name, INPUT_PUSH_FILE);
12210 cmdloop(0);
12211 popfile();
12212}
12213
12214
Denis Vlasenkocc571512007-02-23 21:10:35 +000012215/* ============ find_command inplementation */
12216
12217/*
12218 * Resolve a command name. If you change this routine, you may have to
12219 * change the shellexec routine as well.
12220 */
12221static void
12222find_command(char *name, struct cmdentry *entry, int act, const char *path)
12223{
12224 struct tblentry *cmdp;
12225 int idx;
12226 int prev;
12227 char *fullname;
12228 struct stat statb;
12229 int e;
12230 int updatetbl;
12231 struct builtincmd *bcmd;
12232
12233 /* If name contains a slash, don't use PATH or hash table */
12234 if (strchr(name, '/') != NULL) {
12235 entry->u.index = -1;
12236 if (act & DO_ABS) {
12237 while (stat(name, &statb) < 0) {
12238#ifdef SYSV
12239 if (errno == EINTR)
12240 continue;
12241#endif
12242 entry->cmdtype = CMDUNKNOWN;
12243 return;
12244 }
12245 }
12246 entry->cmdtype = CMDNORMAL;
12247 return;
12248 }
12249
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012250/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012251
12252 updatetbl = (path == pathval());
12253 if (!updatetbl) {
12254 act |= DO_ALTPATH;
12255 if (strstr(path, "%builtin") != NULL)
12256 act |= DO_ALTBLTIN;
12257 }
12258
12259 /* If name is in the table, check answer will be ok */
12260 cmdp = cmdlookup(name, 0);
12261 if (cmdp != NULL) {
12262 int bit;
12263
12264 switch (cmdp->cmdtype) {
12265 default:
12266#if DEBUG
12267 abort();
12268#endif
12269 case CMDNORMAL:
12270 bit = DO_ALTPATH;
12271 break;
12272 case CMDFUNCTION:
12273 bit = DO_NOFUNC;
12274 break;
12275 case CMDBUILTIN:
12276 bit = DO_ALTBLTIN;
12277 break;
12278 }
12279 if (act & bit) {
12280 updatetbl = 0;
12281 cmdp = NULL;
12282 } else if (cmdp->rehash == 0)
12283 /* if not invalidated by cd, we're done */
12284 goto success;
12285 }
12286
12287 /* If %builtin not in path, check for builtin next */
12288 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012289 if (bcmd) {
12290 if (IS_BUILTIN_REGULAR(bcmd))
12291 goto builtin_success;
12292 if (act & DO_ALTPATH) {
12293 if (!(act & DO_ALTBLTIN))
12294 goto builtin_success;
12295 } else if (builtinloc <= 0) {
12296 goto builtin_success;
Denis Vlasenko8e858e22007-03-07 09:35:43 +000012297 }
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000012298 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000012299
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012300#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000012301 {
12302 int applet_no = find_applet_by_name(name);
12303 if (applet_no >= 0) {
12304 entry->cmdtype = CMDNORMAL;
12305 entry->u.index = -2 - applet_no;
12306 return;
12307 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000012308 }
12309#endif
12310
Denis Vlasenkocc571512007-02-23 21:10:35 +000012311 /* We have to search path. */
12312 prev = -1; /* where to start */
12313 if (cmdp && cmdp->rehash) { /* doing a rehash */
12314 if (cmdp->cmdtype == CMDBUILTIN)
12315 prev = builtinloc;
12316 else
12317 prev = cmdp->param.index;
12318 }
12319
12320 e = ENOENT;
12321 idx = -1;
12322 loop:
Denys Vlasenko82a6fb32009-06-14 19:42:12 +020012323 while ((fullname = path_advance(&path, name)) != NULL) {
Denis Vlasenkocc571512007-02-23 21:10:35 +000012324 stunalloc(fullname);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012325 /* NB: code below will still use fullname
12326 * despite it being "unallocated" */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012327 idx++;
12328 if (pathopt) {
12329 if (prefix(pathopt, "builtin")) {
12330 if (bcmd)
12331 goto builtin_success;
12332 continue;
Denis Vlasenko4a9ca132008-04-12 20:07:08 +000012333 }
12334 if ((act & DO_NOFUNC)
12335 || !prefix(pathopt, "func")
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020012336 ) { /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012337 continue;
12338 }
12339 }
12340 /* if rehash, don't redo absolute path names */
12341 if (fullname[0] == '/' && idx <= prev) {
12342 if (idx < prev)
12343 continue;
12344 TRACE(("searchexec \"%s\": no change\n", name));
12345 goto success;
12346 }
12347 while (stat(fullname, &statb) < 0) {
12348#ifdef SYSV
12349 if (errno == EINTR)
12350 continue;
12351#endif
12352 if (errno != ENOENT && errno != ENOTDIR)
12353 e = errno;
12354 goto loop;
12355 }
12356 e = EACCES; /* if we fail, this will be the error */
12357 if (!S_ISREG(statb.st_mode))
12358 continue;
12359 if (pathopt) { /* this is a %func directory */
12360 stalloc(strlen(fullname) + 1);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000012361 /* NB: stalloc will return space pointed by fullname
12362 * (because we don't have any intervening allocations
12363 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012364 readcmdfile(fullname);
12365 cmdp = cmdlookup(name, 0);
12366 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
12367 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
12368 stunalloc(fullname);
12369 goto success;
12370 }
12371 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
12372 if (!updatetbl) {
12373 entry->cmdtype = CMDNORMAL;
12374 entry->u.index = idx;
12375 return;
12376 }
12377 INT_OFF;
12378 cmdp = cmdlookup(name, 1);
12379 cmdp->cmdtype = CMDNORMAL;
12380 cmdp->param.index = idx;
12381 INT_ON;
12382 goto success;
12383 }
12384
12385 /* We failed. If there was an entry for this command, delete it */
12386 if (cmdp && updatetbl)
12387 delete_cmd_entry();
12388 if (act & DO_ERR)
12389 ash_msg("%s: %s", name, errmsg(e, "not found"));
12390 entry->cmdtype = CMDUNKNOWN;
12391 return;
12392
12393 builtin_success:
12394 if (!updatetbl) {
12395 entry->cmdtype = CMDBUILTIN;
12396 entry->u.cmd = bcmd;
12397 return;
12398 }
12399 INT_OFF;
12400 cmdp = cmdlookup(name, 1);
12401 cmdp->cmdtype = CMDBUILTIN;
12402 cmdp->param.cmd = bcmd;
12403 INT_ON;
12404 success:
12405 cmdp->rehash = 0;
12406 entry->cmdtype = cmdp->cmdtype;
12407 entry->u = cmdp->param;
12408}
12409
12410
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012411/* ============ trap.c */
Eric Andersenc470f442003-07-28 09:56:35 +000012412
Eric Andersencb57d552001-06-28 07:25:16 +000012413/*
Eric Andersencb57d552001-06-28 07:25:16 +000012414 * The trap builtin.
12415 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012416static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012417trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012418{
12419 char *action;
12420 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012421 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012422
Eric Andersenc470f442003-07-28 09:56:35 +000012423 nextopt(nullstr);
12424 ap = argptr;
12425 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012426 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012427 char *tr = trap_ptr[signo];
12428 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012429 /* note: bash adds "SIG", but only if invoked
12430 * as "bash". If called as "sh", or if set -o posix,
12431 * then it prints short signal names.
12432 * We are printing short names: */
12433 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012434 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012435 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012436 /* trap_ptr != trap only if we are in special-cased `trap` code.
12437 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020012438 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012439 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000012440 }
12441 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012442 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012443 if (trap_ptr != trap) {
12444 free(trap_ptr);
12445 trap_ptr = trap;
12446 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020012447 */
Eric Andersencb57d552001-06-28 07:25:16 +000012448 return 0;
12449 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020012450
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000012451 action = NULL;
12452 if (ap[1])
Eric Andersencb57d552001-06-28 07:25:16 +000012453 action = *ap++;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012454 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012455 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012456 signo = get_signum(*ap);
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012457 if (signo < 0) {
12458 /* Mimic bash message exactly */
12459 ash_msg("%s: invalid signal specification", *ap);
12460 exitcode = 1;
12461 goto next;
12462 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000012463 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000012464 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000012465 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000012466 action = NULL;
12467 else
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012468 action = ckstrdup(action);
Eric Andersencb57d552001-06-28 07:25:16 +000012469 }
Denis Vlasenko60818682007-09-28 22:07:23 +000012470 free(trap[signo]);
Denys Vlasenko238bf182010-05-18 15:49:07 +020012471 if (action)
12472 may_have_traps = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012473 trap[signo] = action;
12474 if (signo != 0)
12475 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012476 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012477 next:
Eric Andersencb57d552001-06-28 07:25:16 +000012478 ap++;
12479 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010012480 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000012481}
12482
Eric Andersenc470f442003-07-28 09:56:35 +000012483
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012484/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000012485
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000012486#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012487/*
12488 * Lists available builtins
12489 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012490static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012491helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012492{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000012493 unsigned col;
12494 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000012495
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020012496 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000012497 "Built-in commands:\n"
12498 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000012499 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012500 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000012501 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012502 if (col > 60) {
12503 out1fmt("\n");
12504 col = 0;
12505 }
12506 }
Denis Vlasenko80d14be2007-04-10 23:03:30 +000012507#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000012508 {
12509 const char *a = applet_names;
12510 while (*a) {
12511 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
12512 if (col > 60) {
12513 out1fmt("\n");
12514 col = 0;
12515 }
12516 a += strlen(a) + 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012517 }
12518 }
12519#endif
12520 out1fmt("\n\n");
12521 return EXIT_SUCCESS;
12522}
Denis Vlasenko131ae172007-02-18 13:00:19 +000012523#endif /* FEATURE_SH_EXTRA_QUIET */
Eric Andersenc470f442003-07-28 09:56:35 +000012524
Eric Andersencb57d552001-06-28 07:25:16 +000012525/*
Eric Andersencb57d552001-06-28 07:25:16 +000012526 * The export and readonly commands.
12527 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012528static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012529exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000012530{
12531 struct var *vp;
12532 char *name;
12533 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000012534 char **aptr;
Denis Vlasenkob7304742008-10-20 08:15:51 +000012535 int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
Eric Andersencb57d552001-06-28 07:25:16 +000012536
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012537 if (nextopt("p") != 'p') {
12538 aptr = argptr;
12539 name = *aptr;
12540 if (name) {
12541 do {
12542 p = strchr(name, '=');
12543 if (p != NULL) {
12544 p++;
12545 } else {
12546 vp = *findvar(hashvar(name), name);
12547 if (vp) {
12548 vp->flags |= flag;
12549 continue;
12550 }
Eric Andersencb57d552001-06-28 07:25:16 +000012551 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012552 setvar(name, p, flag);
12553 } while ((name = *++aptr) != NULL);
12554 return 0;
12555 }
Eric Andersencb57d552001-06-28 07:25:16 +000012556 }
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012557 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000012558 return 0;
12559}
12560
Eric Andersencb57d552001-06-28 07:25:16 +000012561/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012562 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000012563 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012564static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012565unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000012566{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012567 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000012568
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012569 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012570 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000012571 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000012572}
12573
Eric Andersencb57d552001-06-28 07:25:16 +000012574/*
Eric Andersencb57d552001-06-28 07:25:16 +000012575 * The unset builtin command. We unset the function before we unset the
12576 * variable to allow a function to be unset when there is a readonly variable
12577 * with the same name.
12578 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012579static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012580unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000012581{
12582 char **ap;
12583 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000012584 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012585 int ret = 0;
12586
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012587 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012588 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000012589 }
Eric Andersencb57d552001-06-28 07:25:16 +000012590
Denis Vlasenko2da584f2007-02-19 22:44:05 +000012591 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000012592 if (flag != 'f') {
12593 i = unsetvar(*ap);
12594 ret |= i;
12595 if (!(i & 2))
12596 continue;
12597 }
12598 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000012599 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000012600 }
Eric Andersenc470f442003-07-28 09:56:35 +000012601 return ret & 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012602}
12603
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012604static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012605 ' ', offsetof(struct tms, tms_utime),
12606 '\n', offsetof(struct tms, tms_stime),
12607 ' ', offsetof(struct tms, tms_cutime),
12608 '\n', offsetof(struct tms, tms_cstime),
12609 0
12610};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012611static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012612timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012613{
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012614 unsigned long clk_tck, s, t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012615 const unsigned char *p;
12616 struct tms buf;
12617
12618 clk_tck = sysconf(_SC_CLK_TCK);
Eric Andersencb57d552001-06-28 07:25:16 +000012619 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012620
12621 p = timescmd_str;
12622 do {
12623 t = *(clock_t *)(((char *) &buf) + p[1]);
12624 s = t / clk_tck;
Denys Vlasenko8cd9f342010-06-18 15:36:48 +020012625 t = t % clk_tck;
12626 out1fmt("%lum%lu.%03lus%c",
12627 s / 60, s % 60,
12628 (t * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012629 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012630 p += 2;
12631 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000012632
Eric Andersencb57d552001-06-28 07:25:16 +000012633 return 0;
12634}
12635
Mike Frysinger98c52642009-04-02 10:02:37 +000012636#if ENABLE_SH_MATH_SUPPORT
Eric Andersenc470f442003-07-28 09:56:35 +000012637/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020012638 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012639 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000012640 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012641 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000012642 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012643static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012644letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012645{
Denis Vlasenko68404f12008-03-17 09:00:54 +000012646 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000012647
Denis Vlasenko68404f12008-03-17 09:00:54 +000012648 argv++;
12649 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000012650 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000012651 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000012652 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000012653 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012654
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000012655 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000012656}
Eric Andersenc470f442003-07-28 09:56:35 +000012657#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000012658
Eric Andersenc470f442003-07-28 09:56:35 +000012659/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012660 * The read builtin. Options:
12661 * -r Do not interpret '\' specially
12662 * -s Turn off echo (tty only)
12663 * -n NCHARS Read NCHARS max
12664 * -p PROMPT Display PROMPT on stderr (if input is from tty)
12665 * -t SECONDS Timeout after SECONDS (tty or pipe only)
12666 * -u FD Read from given FD instead of fd 0
Eric Andersenc470f442003-07-28 09:56:35 +000012667 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012668 * TODO: bash also has:
12669 * -a ARRAY Read into array[0],[1],etc
12670 * -d DELIM End on DELIM char, not newline
12671 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000012672 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012673static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012674readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000012675{
Denys Vlasenko73067272010-01-12 22:11:24 +010012676 char *opt_n = NULL;
12677 char *opt_p = NULL;
12678 char *opt_t = NULL;
12679 char *opt_u = NULL;
12680 int read_flags = 0;
12681 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000012682 int i;
12683
Denys Vlasenko73067272010-01-12 22:11:24 +010012684 while ((i = nextopt("p:u:rt:n:s")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000012685 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000012686 case 'p':
Denys Vlasenko73067272010-01-12 22:11:24 +010012687 opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012688 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012689 case 'n':
Denys Vlasenko73067272010-01-12 22:11:24 +010012690 opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012691 break;
12692 case 's':
Denys Vlasenko73067272010-01-12 22:11:24 +010012693 read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000012694 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012695 case 't':
Denys Vlasenko73067272010-01-12 22:11:24 +010012696 opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000012697 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012698 case 'r':
Denys Vlasenko73067272010-01-12 22:11:24 +010012699 read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000012700 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012701 case 'u':
Denys Vlasenko73067272010-01-12 22:11:24 +010012702 opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000012703 break;
Paul Fox02eb9342005-09-07 16:56:02 +000012704 default:
12705 break;
12706 }
Eric Andersenc470f442003-07-28 09:56:35 +000012707 }
Paul Fox02eb9342005-09-07 16:56:02 +000012708
Denys Vlasenko03dad222010-01-12 23:29:57 +010012709 r = shell_builtin_read(setvar2,
Denys Vlasenko73067272010-01-12 22:11:24 +010012710 argptr,
12711 bltinlookup("IFS"), /* can be NULL */
12712 read_flags,
12713 opt_n,
12714 opt_p,
12715 opt_t,
12716 opt_u
12717 );
Denis Vlasenko46aeab92009-03-31 19:18:17 +000012718
Denys Vlasenko73067272010-01-12 22:11:24 +010012719 if ((uintptr_t)r > 1)
12720 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000012721
Denys Vlasenko73067272010-01-12 22:11:24 +010012722 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000012723}
12724
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012725static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012726umaskcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012727{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012728 static const char permuser[3] ALIGN1 = "ugo";
12729 static const char permmode[3] ALIGN1 = "rwx";
12730 static const short permmask[] ALIGN2 = {
Eric Andersenc470f442003-07-28 09:56:35 +000012731 S_IRUSR, S_IWUSR, S_IXUSR,
12732 S_IRGRP, S_IWGRP, S_IXGRP,
12733 S_IROTH, S_IWOTH, S_IXOTH
12734 };
12735
Denis Vlasenkoeb858492009-04-18 02:06:54 +000012736 /* TODO: use bb_parse_mode() instead */
12737
Eric Andersenc470f442003-07-28 09:56:35 +000012738 char *ap;
12739 mode_t mask;
12740 int i;
12741 int symbolic_mode = 0;
12742
12743 while (nextopt("S") != '\0') {
12744 symbolic_mode = 1;
12745 }
12746
Denis Vlasenkob012b102007-02-19 22:43:01 +000012747 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000012748 mask = umask(0);
12749 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000012750 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000012751
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012752 ap = *argptr;
12753 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000012754 if (symbolic_mode) {
12755 char buf[18];
12756 char *p = buf;
12757
12758 for (i = 0; i < 3; i++) {
12759 int j;
12760
12761 *p++ = permuser[i];
12762 *p++ = '=';
12763 for (j = 0; j < 3; j++) {
12764 if ((mask & permmask[3 * i + j]) == 0) {
12765 *p++ = permmode[j];
12766 }
12767 }
12768 *p++ = ',';
12769 }
12770 *--p = 0;
12771 puts(buf);
12772 } else {
12773 out1fmt("%.4o\n", mask);
12774 }
12775 } else {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012776 if (isdigit((unsigned char) *ap)) {
Eric Andersenc470f442003-07-28 09:56:35 +000012777 mask = 0;
12778 do {
12779 if (*ap >= '8' || *ap < '0')
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020012780 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersenc470f442003-07-28 09:56:35 +000012781 mask = (mask << 3) + (*ap - '0');
12782 } while (*++ap != '\0');
12783 umask(mask);
12784 } else {
12785 mask = ~mask & 0777;
12786 if (!bb_parse_mode(ap, &mask)) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000012787 ash_msg_and_raise_error("illegal mode: %s", ap);
Eric Andersenc470f442003-07-28 09:56:35 +000012788 }
12789 umask(~mask & 0777);
12790 }
12791 }
12792 return 0;
12793}
12794
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020012795static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012796ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000012797{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010012798 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000012799}
12800
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012801/* ============ main() and helpers */
12802
12803/*
12804 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012805 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012806static void exitshell(void) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012807static void
12808exitshell(void)
12809{
12810 struct jmploc loc;
12811 char *p;
12812 int status;
12813
12814 status = exitstatus;
12815 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
12816 if (setjmp(loc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000012817 if (exception_type == EXEXIT)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012818/* dash bug: it just does _exit(exitstatus) here
12819 * but we have to do setjobctl(0) first!
12820 * (bug is still not fixed in dash-0.5.3 - if you run dash
12821 * under Midnight Commander, on exit from dash MC is backgrounded) */
12822 status = exitstatus;
12823 goto out;
12824 }
12825 exception_handler = &loc;
12826 p = trap[0];
12827 if (p) {
12828 trap[0] = NULL;
12829 evalstring(p, 0);
Denys Vlasenko0800e3a2009-09-24 03:09:26 +020012830 free(p);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012831 }
12832 flush_stdout_stderr();
12833 out:
12834 setjobctl(0);
12835 _exit(status);
12836 /* NOTREACHED */
12837}
12838
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000012839static void
12840init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012841{
12842 /* from input.c: */
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020012843 /* we will never free this */
12844 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012845
12846 /* from trap.c: */
12847 signal(SIGCHLD, SIG_DFL);
Denys Vlasenko7a7b0342009-12-04 04:18:31 +010012848 /* bash re-enables SIGHUP which is SIG_IGNed on entry.
12849 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
12850 */
Denys Vlasenkocacb2cd2010-10-05 00:13:02 +020012851 signal(SIGHUP, SIG_DFL);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012852
12853 /* from var.c: */
12854 {
12855 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012856 const char *p;
12857 struct stat st1, st2;
12858
12859 initvar();
12860 for (envp = environ; envp && *envp; envp++) {
12861 if (strchr(*envp, '=')) {
12862 setvareq(*envp, VEXPORT|VTEXTFIXED);
12863 }
12864 }
12865
Denys Vlasenko7bb346f2009-10-06 22:09:50 +020012866 setvar("PPID", utoa(getppid()), 0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012867
12868 p = lookupvar("PWD");
12869 if (p)
12870 if (*p != '/' || stat(p, &st1) || stat(".", &st2)
12871 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
12872 p = '\0';
12873 setpwd(p, 0);
12874 }
12875}
12876
12877/*
12878 * Process the shell command line arguments.
12879 */
12880static void
Denis Vlasenko68404f12008-03-17 09:00:54 +000012881procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012882{
12883 int i;
12884 const char *xminusc;
12885 char **xargv;
12886
12887 xargv = argv;
12888 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000012889 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012890 xargv++;
12891 for (i = 0; i < NOPTS; i++)
12892 optlist[i] = 2;
12893 argptr = xargv;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000012894 if (options(1)) {
12895 /* it already printed err message */
12896 raise_exception(EXERROR);
12897 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012898 xargv = argptr;
12899 xminusc = minusc;
12900 if (*xargv == NULL) {
12901 if (xminusc)
12902 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
12903 sflag = 1;
12904 }
12905 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
12906 iflag = 1;
12907 if (mflag == 2)
12908 mflag = iflag;
12909 for (i = 0; i < NOPTS; i++)
12910 if (optlist[i] == 2)
12911 optlist[i] = 0;
12912#if DEBUG == 2
12913 debug = 1;
12914#endif
12915 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
12916 if (xminusc) {
12917 minusc = *xargv++;
12918 if (*xargv)
12919 goto setarg0;
12920 } else if (!sflag) {
12921 setinputfile(*xargv, 0);
12922 setarg0:
12923 arg0 = *xargv++;
12924 commandname = arg0;
12925 }
12926
12927 shellparam.p = xargv;
12928#if ENABLE_ASH_GETOPTS
12929 shellparam.optind = 1;
12930 shellparam.optoff = -1;
12931#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000012932 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012933 while (*xargv) {
12934 shellparam.nparam++;
12935 xargv++;
12936 }
12937 optschanged();
12938}
12939
12940/*
12941 * Read /etc/profile or .profile.
12942 */
12943static void
12944read_profile(const char *name)
12945{
12946 int skip;
12947
12948 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
12949 return;
12950 skip = cmdloop(0);
12951 popfile();
12952 if (skip)
12953 exitshell();
12954}
12955
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012956/*
12957 * This routine is called when an error or an interrupt occurs in an
12958 * interactive shell and control is returned to the main command loop.
12959 */
12960static void
12961reset(void)
12962{
12963 /* from eval.c: */
12964 evalskip = 0;
12965 loopnest = 0;
12966 /* from input.c: */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000012967 g_parsefile->left_in_buffer = 0;
12968 g_parsefile->left_in_line = 0; /* clear input buffer */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012969 popallfiles();
12970 /* from parser.c: */
12971 tokpushback = 0;
12972 checkkwd = 0;
12973 /* from redir.c: */
Denis Vlasenko34c73c42008-08-16 11:48:02 +000012974 clearredir(/*drop:*/ 0);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000012975}
12976
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012977#if PROFILE
12978static short profile_buf[16384];
12979extern int etext();
12980#endif
12981
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000012982/*
12983 * Main routine. We initialize things, parse the arguments, execute
12984 * profiles if we're a login shell, and then call cmdloop to execute
12985 * commands. The setjmp call sets up the location to jump to when an
12986 * exception occurs. When an exception occurs the variable "state"
12987 * is used to figure out how far we had gotten.
12988 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000012989int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000012990int ash_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012991{
Mike Frysinger98c52642009-04-02 10:02:37 +000012992 const char *shinit;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000012993 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012994 struct jmploc jmploc;
12995 struct stackmark smark;
12996
Denis Vlasenko01631112007-12-16 17:20:38 +000012997 /* Initialize global data */
12998 INIT_G_misc();
12999 INIT_G_memstack();
13000 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013001#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000013002 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000013003#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000013004 INIT_G_cmdtable();
13005
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013006#if PROFILE
13007 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
13008#endif
13009
13010#if ENABLE_FEATURE_EDITING
13011 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
13012#endif
13013 state = 0;
13014 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013015 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000013016 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013017
13018 reset();
13019
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013020 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013021 if (e == EXERROR)
13022 exitstatus = 2;
13023 s = state;
Denys Vlasenkob563f622010-09-25 17:15:13 +020013024 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013025 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020013026 }
13027 if (e == EXINT) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013028 outcslow('\n', stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020013029 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000013030
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013031 popstackmark(&smark);
13032 FORCE_INT_ON; /* enable interrupts */
13033 if (s == 1)
13034 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013035 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013036 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013037 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013038 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013039 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013040 }
13041 exception_handler = &jmploc;
13042#if DEBUG
13043 opentrace();
Denis Vlasenko653d8e72009-03-19 21:59:35 +000013044 TRACE(("Shell args: "));
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000013045 trace_puts_args(argv);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013046#endif
13047 rootpid = getpid();
13048
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013049 init();
13050 setstackmark(&smark);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013051 procargs(argv);
13052
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013053#if ENABLE_FEATURE_EDITING_SAVEHISTORY
13054 if (iflag) {
13055 const char *hp = lookupvar("HISTFILE");
13056
13057 if (hp == NULL) {
13058 hp = lookupvar("HOME");
13059 if (hp != NULL) {
13060 char *defhp = concat_path_file(hp, ".ash_history");
13061 setvar("HISTFILE", defhp, 0);
13062 free(defhp);
13063 }
13064 }
13065 }
13066#endif
Denys Vlasenko6088e132010-12-25 23:58:42 +010013067 if (argv[0] && argv[0][0] == '-')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013068 isloginsh = 1;
13069 if (isloginsh) {
13070 state = 1;
13071 read_profile("/etc/profile");
13072 state1:
13073 state = 2;
13074 read_profile(".profile");
13075 }
13076 state2:
13077 state = 3;
13078 if (
13079#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013080 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013081#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013082 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013083 ) {
13084 shinit = lookupvar("ENV");
13085 if (shinit != NULL && *shinit != '\0') {
13086 read_profile(shinit);
13087 }
13088 }
13089 state3:
13090 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013091 if (minusc) {
13092 /* evalstring pushes parsefile stack.
13093 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000013094 * is one of stacked source fds.
13095 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020013096 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020013097 // ^^ not necessary since now we special-case fd 0
13098 // in is_hidden_fd() to not be considered "hidden fd"
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013099 evalstring(minusc, 0);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013100 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013101
13102 if (sflag || minusc == NULL) {
Denys Vlasenko0337e032009-11-29 00:12:30 +010013103#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000013104 if (iflag) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013105 const char *hp = lookupvar("HISTFILE");
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000013106 if (hp)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013107 line_input_state->hist_file = hp;
13108 }
13109#endif
13110 state4: /* XXX ??? - why isn't this before the "if" statement */
13111 cmdloop(1);
13112 }
13113#if PROFILE
13114 monitor(0);
13115#endif
13116#ifdef GPROF
13117 {
13118 extern void _mcleanup(void);
13119 _mcleanup();
13120 }
13121#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020013122 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013123 exitshell();
13124 /* NOTREACHED */
13125}
13126
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013127
Eric Andersendf82f612001-06-28 07:46:40 +000013128/*-
13129 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000013130 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000013131 *
13132 * This code is derived from software contributed to Berkeley by
13133 * Kenneth Almquist.
13134 *
13135 * Redistribution and use in source and binary forms, with or without
13136 * modification, are permitted provided that the following conditions
13137 * are met:
13138 * 1. Redistributions of source code must retain the above copyright
13139 * notice, this list of conditions and the following disclaimer.
13140 * 2. Redistributions in binary form must reproduce the above copyright
13141 * notice, this list of conditions and the following disclaimer in the
13142 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013143 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000013144 * may be used to endorse or promote products derived from this software
13145 * without specific prior written permission.
13146 *
13147 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13148 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13149 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13150 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13151 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13152 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13153 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13154 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13155 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13156 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13157 * SUCH DAMAGE.
13158 */